diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 4d7a7262697a31..00000000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: [ 'https://openwrt.org/donate' ] diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index beb7787d34b200..00000000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,139 +0,0 @@ -# target/* -"target/airoha": - - "target/linux/airoha/**" -"target/apm821xx": - - "target/linux/apm821xx/**" -"target/archs38": - - "target/linux/archs38/**" -"target/armsr": - - "target/linux/armsr/**" -"target/at91": - - "target/linux/at91/**" - - "package/boot/at91bootstrap/**" - - "package/boot/uboot-at91/**" -"target/ath79": - - "target/linux/ath79/**" -"target/bcm27xx": - - "target/linux/bcm27xx/**" - - "package/kernel/bcm27xx-gpu-fw/**" -"target/bcm47xx": - - "target/linux/bcm47xx/**" -"target/bcm4908": - - "target/linux/bcm4908/**" - - "package/boot/uboot-bcm4908/**" -"target/bcm53xx": - - "target/linux/bcm53xx/**" - - "package/boot/uboot-bcm53xx/**" -"target/bcm63xx": - - "target/linux/bcm63xx/**" - - "package/kernel/bcm63xx-cfe/**" - - "package/boot/arm-trusted-firmware-bcm63xx/**" -"target/bmips": - - "target/linux/bmips/**" - - "package/boot/uboot-bmips/**" -"target/d1": - - "target/linux/d1/**" - - "package/boot/uboot-d1/**" - - "package/boot/opensbi/**" -"target/gemini": - - "target/linux/gemini/**" -"target/imx": - - "target/linux/imx/**" - - "package/boot/imx-bootlets/**" - - "package/boot/uboot-imx/**" -"target/ipq40xx": - - "target/linux/ipq40xx/**" -"target/ipq806x": - - "target/linux/ipq806x/**" -"target/qualcommax": - - "target/linux/qualcommax/**" -"target/kirkwood": - - "target/linux/kirkwood/**" - - "package/boot/uboot-kirkwood/**" -"target/lantiq": - - "target/linux/lantiq/**" - - "package/kernel/lantiq/**" - - "package/firmware/lantiq/**" - - "package/boot/uboot-lantiq/**" -"target/layerscape": - - "target/linux/layerscape/**" - - "package/firmware/layerscape/**" - - "package/boot/tfa-layerscape/**" - - "package/boot/uboot-layerscape/**" - - "package/network/utils/layerscape/**" -"target/malta": - - "target/linux/malta/**" -"target/mediatek": - - "target/linux/mediatek/**" - - "package/boot/arm-trusted-firmware-mediatek/**" - - "package/boot/uboot-mediatek/**" -"target/mpc85xx": - - "target/linux/mpc85xx/**" -"target/mvebu": - - "target/linux/mvebu/**" - - "package/boot/arm-trusted-firmware-mvebu/**" - - "package/boot/uboot-mvebu/**" -"target/mxs": - - "target/linux/mxs/**" - - "package/boot/uboot-mxs/**" -"target/octeon": - - "target/linux/octeon/**" -"target/octeontx": - - "target/linux/octeontx/**" -"target/omap": - - "target/linux/omap/**" - - "package/boot/uboot-omap/**" -"target/pistachio": - - "target/linux/pistachio/**" -"target/qoriq": - - "target/linux/qoriq/**" -"target/ramips": - - "target/linux/ramips/**" -"target/realtek": - - "target/linux/realtek/**" -"target/rockchip": - - "target/linux/rockchip/**" - - "package/boot/arm-trusted-firmware-rockchip/**" - - "package/boot/uboot-rockchip/**" -"target/sifiveu": - - "target/linux/sifiveu/**" - - "package/boot/uboot-sifiveu/**" - - "package/boot/opensbi/**" -"target/sunxi": - - "target/linux/sunxi/**" - - "package/boot/arm-trusted-firmware-sunxi/**" - - "package/boot/uboot-sunxi/**" -"target/tegra": - - "target/linux/tegra/**" - - "package/boot/uboot-tegra/**" -"target/uml": - - "target/linux/uml/**" -"target/x86": - - "target/linux/x86/**" -"target/zynq": - - "target/linux/zynq/**" - - "package/boot/uboot-zynq/**" -# target/imagebuilder -"target/imagebuilder": - - "target/imagebuilder/**" -# kernel -"kernel": - - "target/linux/generic/**" - - "target/linux/**/config-*" - - "target/linux/**/patches-*" - - "target/linux/**/files/**" - - "package/kernel/linux/**" -# core packages -"core packages": - - "package/**" -# build/scripts/tools -"build/scripts/tools": - - "include/**" - - "scripts/**" - - "tools/**" -# toolchain -"toolchain": - - "toolchain/**" -# GitHub/CI -"GitHub/CI": - - ".github/**" diff --git a/.github/pull_request_template b/.github/pull_request_template deleted file mode 100644 index a2d6bd185ff61d..00000000000000 --- a/.github/pull_request_template +++ /dev/null @@ -1,8 +0,0 @@ -Thanks for your contribution to OpenWrt! - -To help keep the codebase consistent and readable, -and to help people review your contribution, -we ask you to follow the rules you find in the wiki at this link -https://openwrt.org/submitting-patches - -Please remove this message before posting the pull request. diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml deleted file mode 100644 index f54695986daace..00000000000000 --- a/.github/workflows/coverity.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Coverity scan build - -on: - schedule: - - cron: '30 2 * * 6' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - -jobs: - coverity_build: - name: Coverity x86/64 build - secrets: - coverity_api_token: ${{ secrets.COVERITY_API_TOKEN }} - permissions: - contents: read - packages: read - actions: write - uses: openwrt/actions-shared-workflows/.github/workflows/coverity.yml@main diff --git a/.github/workflows/kernel.yml b/.github/workflows/kernel.yml deleted file mode 100644 index 43c610ab4f00cb..00000000000000 --- a/.github/workflows/kernel.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build Kernel - -on: - pull_request: - paths: - - '.github/workflows/check-kernel-patches.yml' - - '.github/workflows/build.yml' - - '.github/workflows/kernel.yml' - - 'include/kernel*' - - 'package/kernel/**' - - 'target/linux/**' - push: - paths: - - '.github/workflows/check-kernel-patches.yml' - - '.github/workflows/build.yml' - - '.github/workflows/kernel.yml' - - 'include/kernel*' - - 'package/kernel/**' - - 'target/linux/**' - branches-ignore: - - master - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - build-kernels: - name: Build all affected Kernels - permissions: - contents: read - packages: read - actions: write - secrets: - ccache_s3_endpoint: ${{ secrets.CCACHE_S3_ENDPOINT }} - ccache_s3_bucket: ${{ secrets.CCACHE_S3_BUCKET }} - ccache_s3_access_key: ${{ secrets.CCACHE_S3_ACCESS_KEY }} - ccache_s3_secret_key: ${{ secrets.CCACHE_S3_SECRET_KEY }} - uses: openwrt/actions-shared-workflows/.github/workflows/kernel.yml@main diff --git a/.github/workflows/label-kernel.yml b/.github/workflows/label-kernel.yml deleted file mode 100644 index 655d7d6ff371b5..00000000000000 --- a/.github/workflows/label-kernel.yml +++ /dev/null @@ -1,16 +0,0 @@ -# ci:kernel:x86:64 is going to trigger CI kernel check jobs for x86/64 target - -name: Build kernel and check patches for target specified in labels -on: - pull_request: - types: - - labeled - -jobs: - build-kernels-label: - name: Build all affected Kernels from defined label - permissions: - contents: read - packages: read - actions: write - uses: openwrt/actions-shared-workflows/.github/workflows/label-kernel.yml@main diff --git a/.github/workflows/label-target.yml b/.github/workflows/label-target.yml deleted file mode 100644 index 989ee9fcb43491..00000000000000 --- a/.github/workflows/label-target.yml +++ /dev/null @@ -1,16 +0,0 @@ -# ci:target:x86:64 is going to trigger CI target check jobs for x86/64 target - -name: Build check target specified in labels -on: - pull_request: - types: - - labeled - -jobs: - build-target-label: - name: Build target from defined label - permissions: - contents: read - packages: read - actions: write - uses: openwrt/actions-shared-workflows/.github/workflows/label-target.yml@main diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index 5f82b88a50a7dc..00000000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: 'Pull Request Labeler' -on: - - pull_request_target - -permissions: - contents: read - -jobs: - labeler: - permissions: - contents: read # to determine modified files (actions/labeler) - pull-requests: write # to add labels to PRs (actions/labeler) - - name: Pull Request Labeler - runs-on: ubuntu-latest - steps: - - uses: actions/labeler@v4 - with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' - - - name: Check Branch - id: check-branch - run: | - if echo "${{ github.base_ref }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]'; then - echo "release-tag=$(echo ${{ github.base_ref }} | sed 's/openwrt-/release\//')" >> $GITHUB_OUTPUT - fi - - - uses: buildsville/add-remove-label@v2.0.0 - if: ${{ steps.check-branch.outputs.release-tag }} - with: - token: ${{secrets.GITHUB_TOKEN}} - labels: ${{ steps.check-branch.outputs.release-tag }} - type: add diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml deleted file mode 100644 index f061c1cd371a5d..00000000000000 --- a/.github/workflows/packages.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build all core packages - -on: - pull_request: - paths: - - '.github/workflows/packages.yml' - - 'config/**' - - 'include/**' - - 'package/**' - - 'target/linux/generic/**' - - 'toolchain/**' - push: - paths: - - '.github/workflows/packages.yml' - - 'config/**' - - 'include/**' - - 'package/**' - - 'target/linux/generic/**' - - 'toolchain/**' - branches-ignore: - - master - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - build-packages: - name: Build all core packages for selected target - permissions: - contents: read - packages: read - actions: write - secrets: - ccache_s3_endpoint: ${{ secrets.CCACHE_S3_ENDPOINT }} - ccache_s3_bucket: ${{ secrets.CCACHE_S3_BUCKET }} - ccache_s3_access_key: ${{ secrets.CCACHE_S3_ACCESS_KEY }} - ccache_s3_secret_key: ${{ secrets.CCACHE_S3_SECRET_KEY }} - uses: openwrt/actions-shared-workflows/.github/workflows/packages.yml@main diff --git a/.github/workflows/push-containers.yml b/.github/workflows/push-containers.yml deleted file mode 100644 index 74f534d57a4a40..00000000000000 --- a/.github/workflows/push-containers.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Build and Push prebuilt tools container - -on: - push: - paths: - - 'include/version.mk' - - 'include/cmake.mk' - - 'tools/**' - - '.github/workflows/push-containers.yml' - - 'toolchain/**' - branches-ignore: - - master - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build-push-containers: - name: Build and Push all prebuilt containers - permissions: - contents: read - packages: write - actions: write - uses: openwrt/actions-shared-workflows/.github/workflows/push-containers.yml@main diff --git a/.github/workflows/scripts/ci_helpers.sh b/.github/workflows/scripts/ci_helpers.sh deleted file mode 100644 index 2f9daf81e2bad6..00000000000000 --- a/.github/workflows/scripts/ci_helpers.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -color_out() { - printf "\e[0;$1m%s\e[0;0m\n" "$2" -} - -success() { - color_out 32 "$1" -} - -info() { - color_out 36 "$1" -} - -err() { - color_out 31 "$1" -} - -warn() { - color_out 33 "$1" -} - -err_die() { - err "$1" - exit 1 -} diff --git a/.github/workflows/scripts/show_build_failures.sh b/.github/workflows/scripts/show_build_failures.sh deleted file mode 100755 index 7b1a021155bdc1..00000000000000 --- a/.github/workflows/scripts/show_build_failures.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -original_exit_code="${ret:-1}" -log_dir_path="${1:-logs}" -context="${2:-10}" - -show_make_build_errors() { - grep -slr 'make\[[[:digit:]]\+\].*Error [[:digit:]]\+$' "$log_dir_path" | while IFS= read -r log_file; do - printf "====== Make errors from %s ======\n" "$log_file"; - grep -r -C"$context" 'make\[[[:digit:]]\+\].*Error [[:digit:]]\+$' "$log_file" ; - done -} - -show_make_build_errors -exit "$original_exit_code" diff --git a/.github/workflows/toolchain.yml b/.github/workflows/toolchain.yml deleted file mode 100644 index 0bc9fec1795f76..00000000000000 --- a/.github/workflows/toolchain.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build Toolchains - -on: - pull_request: - paths: - - '.github/workflows/toolchain.yml' - - 'toolchain/**' - push: - paths: - - '.github/workflows/toolchain.yml' - - 'toolchain/**' - branches-ignore: - - master - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - build-toolchains: - name: Build Toolchains for each target - permissions: - contents: read - packages: read - actions: write - uses: openwrt/actions-shared-workflows/.github/workflows/toolchain.yml@main diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml deleted file mode 100644 index 35904a5e564f65..00000000000000 --- a/.github/workflows/tools.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Build host tools - -on: - pull_request: - paths: - - 'include/**' - - 'tools/**' - - '.github/workflows/tools.yml' - push: - paths: - - 'include/**' - - 'tools/**' - - '.github/workflows/tools.yml' - branches-ignore: - - master - -permissions: - contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - build-tools: - name: Build host tools for linux and macos based systems - uses: openwrt/actions-shared-workflows/.github/workflows/tools.yml@main diff --git a/feeds.conf.default b/feeds.conf.default index fc679335e0e47f..5e40f047d6b5f4 100644 --- a/feeds.conf.default +++ b/feeds.conf.default @@ -2,6 +2,8 @@ src-git packages https://git.openwrt.org/feed/packages.git src-git luci https://git.openwrt.org/project/luci.git src-git routing https://git.openwrt.org/feed/routing.git src-git telephony https://git.openwrt.org/feed/telephony.git +src-git nss_packages https://github.com/qosmio/nss-packages.git;NSS-12.5-K6.x +src-git sqm_scripts_nss https://github.com/qosmio/sqm-scripts-nss.git #src-git video https://github.com/openwrt/video.git #src-git targets https://github.com/openwrt/targets.git #src-git oldpackages http://git.openwrt.org/packages.git diff --git a/package/devel/perf/Makefile b/package/devel/perf/Makefile index 20be59516dad12..62fd7817c3c350 100644 --- a/package/devel/perf/Makefile +++ b/package/devel/perf/Makefile @@ -56,6 +56,8 @@ MAKE_FLAGS = \ NO_LIBUNWIND=1 \ NO_LIBZSTD=1 \ NO_LIBCAP=1 \ + NO_LIBTRACEEVENT=1 \ + NO_LIBBPF=1 \ CROSS_COMPILE="$(TARGET_CROSS)" \ CC="$(TARGET_CC)" \ LD="$(TARGET_CROSS)ld" \ diff --git a/package/kernel/ath10k-ct/patches/999-003-ath10k-add-nss-support.patch b/package/kernel/ath10k-ct/patches/999-003-ath10k-add-nss-support.patch new file mode 100644 index 00000000000000..2ef718625f011e --- /dev/null +++ b/package/kernel/ath10k-ct/patches/999-003-ath10k-add-nss-support.patch @@ -0,0 +1,32 @@ +--- a/ath10k-6.4/mac.c ++++ b/ath10k-6.4/mac.c +@@ -6362,13 +6362,13 @@ + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac set txbf conf, value: 0x%x\n", + value); + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + ar->wmi.vdev_param->txbf, value); + } + +-static void ath10k_update_vif_offload(struct ieee80211_hw *hw, ++static int ath10k_update_vif_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) + { + struct ath10k_vif *arvif = (void *)vif->drv_priv; + struct ath10k *ar = hw->priv; + u32 vdev_param; + int ret; +@@ -6384,14 +6384,16 @@ + ATH10K_HW_TXRX_NATIVE_WIFI); + /* 10.X firmware does not support this VDEV parameter. Do not warn */ + if (ret && ret != -EOPNOTSUPP) { + ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n", + arvif->vdev_id, ret); + } ++ ++ return ret; + } + + /* + * TODO: + * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE, + * because we will send mgmt frames without CCK. This requirement diff --git a/package/kernel/cryptodev-linux/Makefile b/package/kernel/cryptodev-linux/Makefile index ea1bd241617b7d..6fc511133f9439 100644 --- a/package/kernel/cryptodev-linux/Makefile +++ b/package/kernel/cryptodev-linux/Makefile @@ -40,13 +40,22 @@ define KernelPackage/cryptodev/description hardware ciphers by user-space applications. endef +ifneq ($(CONFIG_PACKAGE_kmod-crypto-qce),) +EXTRA_CFLAGS+=-DQCA +endif + +ifneq ($(CONFIG_PACKAGE_kmod-qca-nss-crypto),) +EXTRA_CFLAGS+=-DQCANSS +endif + define Build/Configure endef define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) \ $(KERNEL_MAKE_FLAGS) \ - KERNEL_DIR="$(LINUX_DIR)" + KERNEL_DIR="$(LINUX_DIR)" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" endef define Build/InstallDev diff --git a/package/kernel/cryptodev-linux/patches/0005-add-qca-nss.patch b/package/kernel/cryptodev-linux/patches/0005-add-qca-nss.patch new file mode 100644 index 00000000000000..4f467a18773467 --- /dev/null +++ b/package/kernel/cryptodev-linux/patches/0005-add-qca-nss.patch @@ -0,0 +1,99 @@ +--- a/ioctl.c ++++ b/ioctl.c +@@ -829,29 +829,37 @@ static inline void tfm_info_to_alg_info( + "%s", crypto_tfm_alg_driver_name(tfm)); + } + +-#ifndef CRYPTO_ALG_KERN_DRIVER_ONLY ++#if defined(QCANSS) || defined(QCA) || defined(MT7621) || defined(MT7622) || defined(LANTIQ) || defined(BCM675X) || defined(BCM49XX) || defined(MT798X) + static unsigned int is_known_accelerated(struct crypto_tfm *tfm) + { + const char *name = crypto_tfm_alg_driver_name(tfm); + + if (name == NULL) +- return 1; /* assume accelerated */ ++ return 0; + + /* look for known crypto engine names */ +- if (strstr(name, "-talitos") || +- !strncmp(name, "mv-", 3) || +- !strncmp(name, "atmel-", 6) || +- strstr(name, "geode") || +- strstr(name, "hifn") || +- strstr(name, "-ixp4xx") || +- strstr(name, "-omap") || +- strstr(name, "-picoxcell") || +- strstr(name, "-s5p") || +- strstr(name, "-ppc4xx") || +- strstr(name, "-caam") || +- strstr(name, "-n2")) ++#if defined(QCANSS) ++ if (!strncmp(name, "nss-", 4)) + return 1; +- ++#elif defined(QCA) ++ if (!strncmp(name, "qcrypto", 7)) ++ return 1; ++#elif defined(MT7621) ++ if (strstr(name, "eip93")) ++ return 1; ++#elif defined(MT7622) ++ if (strstr(name, "mtk")) ++ return 1; ++#elif defined(MT798X) ++ if (strstr(name, "safexcel-")) ++ return 1; ++#elif defined(LANTIQ) ++ if (strstr(name, "ltq-crypto")) ++ return 1; ++#elif defined(BCM675X) || defined(BCM49XX) ++ if (strstr(name, "-iproc")) ++ return 1; ++#endif + return 0; + } + #endif +@@ -876,22 +884,22 @@ static int get_session_info(struct fcryp + else + tfm = crypto_aead_tfm(ses_ptr->cdata.async.as); + tfm_info_to_alg_info(&siop->cipher_info, tfm); +-#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY +- if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) ++#if defined(QCANSS) || defined(QCA) || defined(MT7621) || defined(MT7622) || defined(LANTIQ) || defined(BCM675X) || defined(BCM49XX) || defined(MT798X) ++ if (is_known_accelerated(tfm)) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; + #else +- if (is_known_accelerated(tfm)) ++ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; + #endif + } + if (ses_ptr->hdata.init) { + tfm = crypto_ahash_tfm(ses_ptr->hdata.async.s); + tfm_info_to_alg_info(&siop->hash_info, tfm); +-#ifdef CRYPTO_ALG_KERN_DRIVER_ONLY +- if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) ++#if defined(QCANSS) || defined(QCA) || defined(MT7621) || defined(MT7622) || defined(LANTIQ) || defined(BCM675X) || defined(BCM49XX) || defined(MT798X) ++ if (is_known_accelerated(tfm)) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; + #else +- if (is_known_accelerated(tfm)) ++ if (tfm->__crt_alg->cra_flags & CRYPTO_ALG_KERN_DRIVER_ONLY) + siop->flags |= SIOP_FLAG_KERNEL_DRIVER_ONLY; + #endif + } +--- a/main.c ++++ b/main.c +@@ -168,6 +168,12 @@ __crypto_run_zc(struct csession *ses_ptr + struct crypt_op *cop = &kcop->cop; + int ret = 0; + ++#if defined(QCANSS) ++//openssl bug!!! ++ if (unlikely(cop->src != cop->dst)) { ++ return __crypto_run_std(ses_ptr, cop); ++ } ++#endif + ret = get_userbuf(ses_ptr, cop->src, cop->len, cop->dst, cop->len, + kcop->task, kcop->mm, &src_sg, &dst_sg); + if (unlikely(ret)) { diff --git a/package/kernel/linux/files/sysctl-nf-conntrack.conf b/package/kernel/linux/files/sysctl-nf-conntrack.conf index c6a0ef362b6a9f..b8821b162ac4b8 100644 --- a/package/kernel/linux/files/sysctl-nf-conntrack.conf +++ b/package/kernel/linux/files/sysctl-nf-conntrack.conf @@ -6,3 +6,6 @@ net.netfilter.nf_conntrack_checksum=0 net.netfilter.nf_conntrack_tcp_timeout_established=7440 net.netfilter.nf_conntrack_udp_timeout=60 net.netfilter.nf_conntrack_udp_timeout_stream=180 +net.netfilter.nf_conntrack_helper=1 +net.netfilter.nf_conntrack_buckets=16384 +net.netfilter.nf_conntrack_expect_max=16384 \ No newline at end of file diff --git a/package/kernel/linux/modules/crypto.mk b/package/kernel/linux/modules/crypto.mk index 4a8a283d377321..f448ea4cb7900f 100644 --- a/package/kernel/linux/modules/crypto.mk +++ b/package/kernel/linux/modules/crypto.mk @@ -1159,3 +1159,20 @@ endef $(eval $(call KernelPackage,crypto-xts)) +define KernelPackage/crypto-qce + TITLE:=QTI Crypto Engine (QCE) + KCONFIG:= \ + CONFIG_CRYPTO_DEV_QCE \ + CONFIG_CRYPTO_DEV_QCE_AEAD=y \ + CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y \ + CONFIG_CRYPTO_DEV_QCE_SHA=y \ + CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y \ + CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 + FILES:= \ + $(LINUX_DIR)/drivers/crypto/qce/qcrypto.ko + AUTOLOAD:=$(call AutoLoad,09,qcrypto) + DEPENDS:=@TARGET_qualcommax +kmod-crypto-manager +kmod-crypto-hash +kmod-crypto-des + $(call AddDepends/crypto) +endef + +$(eval $(call KernelPackage,crypto-qce)) diff --git a/package/kernel/linux/modules/netfilter.mk b/package/kernel/linux/modules/netfilter.mk index da3e69e49ac6f1..42d29d485a3915 100644 --- a/package/kernel/linux/modules/netfilter.mk +++ b/package/kernel/linux/modules/netfilter.mk @@ -312,6 +312,7 @@ $(eval $(call KernelPackage,ipt-offload)) define KernelPackage/ipt-ipopt TITLE:=Modules for matching/changing IP packet options KCONFIG:=$(KCONFIG_IPT_IPOPT) + DEPENDS:=+PACKAGE_kmod-nf-conntrack:kmod-nf-conntrack FILES:=$(foreach mod,$(IPT_IPOPT-m),$(LINUX_DIR)/net/$(mod).ko) AUTOLOAD:=$(call AutoProbe,$(notdir $(IPT_IPOPT-m))) $(call AddDepends/ipt) diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index 566cfdd7b2efcb..cf9c743eb3d198 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=mac80211 PKG_VERSION:=6.6.15 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_LICENSE:=GPL-2.0-only PKG_LICENSE_FILES:=COPYING @@ -36,14 +36,18 @@ PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_MAC80211_DEBUGFS \ CONFIG_PACKAGE_MAC80211_MESH \ CONFIG_PACKAGE_MAC80211_TRACING \ + CONFIG_PACKAGE_MAC80211_NSS_SUPPORT \ + CONFIG_PACKAGE_MAC80211_NSS_REDIRECT \ CONFIG_PACKAGE_IWLWIFI_DEBUG \ CONFIG_PACKAGE_IWLWIFI_DEBUGFS \ - CONFIG_PACKAGE_RTLWIFI_DEBUG \ + CONFIG_PACKAGE_RTLWIFI_DEBUG include $(INCLUDE_DIR)/package.mk WMENU:=Wireless Drivers +NSS_PATCH:= subsys ath10k ath11k + define KernelPackage/mac80211/Default SUBMENU:=$(WMENU) URL:=https://wireless.wiki.kernel.org/ @@ -129,10 +133,15 @@ define KernelPackage/mac80211 $(call KernelPackage/mac80211/Default) TITLE:=Linux 802.11 Wireless Networking Stack # +kmod-crypto-cmac is a runtime only dependency of net/mac80211/aes_cmac.c - DEPENDS+= +kmod-cfg80211 +kmod-crypto-cmac +kmod-crypto-ccm +kmod-crypto-gcm +hostapd-common + DEPENDS+= +kmod-cfg80211 +kmod-crypto-cmac +kmod-crypto-ccm +kmod-crypto-gcm +hostapd-common \ + +ATH11K_NSS_SUPPORT:kmod-qca-nss-drv KCONFIG:=\ CONFIG_AVERAGE=y FILES:= $(PKG_BUILD_DIR)/net/mac80211/mac80211.ko +ifdef CONFIG_PACKAGE_MAC80211_NSS_REDIRECT + AUTOLOAD:=$(call AutoProbe, mac80211) + MODPARAMS.mac80211:=nss_redirect=1 +endif ABI_VERSION:=$(PKG_VERSION)-$(PKG_RELEASE) MENU:=1 endef @@ -140,6 +149,21 @@ endef define KernelPackage/mac80211/config if PACKAGE_kmod-mac80211 + if PACKAGE_kmod-qca-nss-drv + config PACKAGE_MAC80211_NSS_SUPPORT + bool "Enable NSS support" + default y + help + This option enables support for NSS in QCA boards. + config PACKAGE_MAC80211_NSS_REDIRECT + bool "Enable autoloading with 'nss_redirect=1' (not needed on ath11k)" + depends on PACKAGE_MAC80211_NSS_SUPPORT + default n + help + This option enables autoloading mac80211 with 'nss_redirect=1'. + However, it is not required for wifi offloading. + endif + config PACKAGE_MAC80211_DEBUGFS bool "Export mac80211 internals in DebugFS" select KERNEL_DEBUG_FS @@ -281,6 +305,12 @@ ifeq ($(BUILD_VARIANT),smallbuffers) C_DEFINES+= -DCONFIG_ATH10K_SMALLBUFFERS endif +ifdef CONFIG_ATH11K_NSS_SUPPORT + IREMAP_CFLAGS+=-I$(STAGING_DIR)/usr/include/qca-nss-drv -I$(STAGING_DIR)/usr/include/qca-nss-clients +endif + +config-$(CONFIG_PACKAGE_MAC80211_NSS_SUPPORT) += MAC80211_NSS_SUPPORT + MAKE_OPTS:= \ $(subst -C $(LINUX_DIR),-C "$(PKG_BUILD_DIR)",$(KERNEL_MAKEOPTS)) \ EXTRA_CFLAGS="-I$(PKG_BUILD_DIR)/include $(IREMAP_CFLAGS) $(C_DEFINES)" \ @@ -349,6 +379,11 @@ define Build/Patch $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath9k,ath9k/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath10k,ath10k/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath11k,ath11k/) +ifdef CONFIG_ATH11K_NSS_SUPPORT + $(foreach driver,$(NSS_PATCH), + $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/nss/$(driver),nss/$(driver)/) + ) +endif $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rt2x00,rt2x00/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mt7601u,mt7601u/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/) @@ -365,6 +400,11 @@ define Quilt/Refresh/Package $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath9k,ath9k/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath10k,ath10k/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath11k,ath11k/) +ifdef CONFIG_ATH11K_NSS_SUPPORT + $(foreach driver,$(NSS_PATCH), + $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/nss/$(driver),nss/$(driver)/) + ) +endif $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rt2x00,rt2x00/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mt7601u,mt7601u/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/) @@ -391,6 +431,13 @@ define Build/InstallDev rm -f $(1)/usr/include/mac80211-backport/linux/module.h endef +ifdef CONFIG_ATH11K_NSS_SUPPORT +define KernelPackage/ath11k/install + $(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config + $(INSTALL_BIN) ./files/qca-nss-pbuf.init $(1)/etc/init.d/qca-nss-pbuf + $(INSTALL_BIN) ./files/pbuf.uci $(1)/etc/config/pbuf +endef +endif $(eval $(foreach drv,$(PKG_DRIVERS),$(call KernelPackage,$(drv)))) $(eval $(call KernelPackage,cfg80211)) diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index b0c3691a572ae6..ba76c0125a910b 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -13,7 +13,14 @@ PKG_CONFIG_DEPENDS += \ CONFIG_ATH10K_LEDS \ CONFIG_ATH10K_THERMAL \ CONFIG_ATH11K_THERMAL \ - CONFIG_ATH_USER_REGD + CONFIG_ATH11K_DEBUGFS_STA \ + CONFIG_ATH11K_DEBUGFS_HTT_STATS \ + CONFIG_ATH_USER_REGD \ + CONFIG_ATH11K_MEM_PROFILE_1G \ + CONFIG_ATH11K_MEM_PROFILE_512M \ + CONFIG_ATH11K_MEM_PROFILE_256M \ + CONFIG_ATH11K_NSS_SUPPORT \ + CONFIG_ATH11K_NSS_MESH_SUPPORT ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS config-y += \ @@ -56,6 +63,13 @@ config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR config-$(CONFIG_ATH10K_LEDS) += ATH10K_LEDS config-$(CONFIG_ATH10K_THERMAL) += ATH10K_THERMAL config-$(CONFIG_ATH11K_THERMAL) += ATH11K_THERMAL +config-$(CONFIG_ATH11K_MEM_PROFILE_1G) += ATH11K_MEM_PROFILE_1G +config-$(CONFIG_ATH11K_MEM_PROFILE_512M) += ATH11K_MEM_PROFILE_512M +config-$(CONFIG_ATH11K_MEM_PROFILE_256M) += ATH11K_MEM_PROFILE_256M +config-$(CONFIG_ATH11K_NSS_SUPPORT) += ATH11K_NSS_SUPPORT +config-$(CONFIG_ATH11K_NSS_MESH_SUPPORT) += ATH11K_NSS_MESH_SUPPORT +config-$(CONFIG_ATH11K_DEBUGFS_STA) += ATH11K_DEBUGFS_STA +config-$(CONFIG_ATH11K_DEBUGFS_HTT_STATS) += ATH11K_DEBUGFS_HTT_STATS config-$(call config_package,ath9k-htc) += ATH9K_HTC config-$(call config_package,ath10k,regular) += ATH10K ATH10K_PCI @@ -297,9 +311,17 @@ define KernelPackage/ath11k TITLE:=Qualcomm 802.11ax wireless chipset support (common code) URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k DEPENDS+= +kmod-ath +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT \ - +kmod-crypto-michael-mic +ATH11K_THERMAL:kmod-hwmon-core +ATH11K_THERMAL:kmod-thermal + +kmod-crypto-michael-mic +ATH11K_THERMAL:kmod-hwmon-core +ATH11K_THERMAL:kmod-thermal \ + +ATH11K_NSS_SUPPORT:kmod-qca-nss-drv \ + +ATH11K_NSS_MESH_SUPPORT:kmod-qca-nss-drv-wifi-meshmgr \ + +@(ATH11K_NSS_SUPPORT):NSS_DRV_WIFIOFFLOAD_ENABLE \ + +@(ATH11K_NSS_SUPPORT):NSS_DRV_WIFI_EXT_VDEV_ENABLE FILES:=$(PKG_BUILD_DIR)/drivers/soc/qcom/qmi_helpers.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k.ko +ifdef CONFIG_ATH11K_NSS_SUPPORT + AUTOLOAD:=$(call AutoProbe,ath11k) + MODPARAMS.ath11k:=nss_offload=1 frame_mode=2 +endif endef define KernelPackage/ath11k/description @@ -307,6 +329,10 @@ This module adds support for Qualcomm Technologies 802.11ax family of chipsets. endef +define KernelPackage/ath11k/conffiles +/etc/config/pbuf +endef + define KernelPackage/ath11k/config config ATH11K_THERMAL @@ -314,6 +340,63 @@ define KernelPackage/ath11k/config depends on PACKAGE_kmod-ath11k default y if TARGET_qualcommax + config ATH11K_DEBUGFS_STA + bool "Enable ath11k station statistics" + depends on PACKAGE_kmod-ath11k + depends on PACKAGE_MAC80211_DEBUGFS + default y + help + Say Y to enable access to the station statistics via debugfs. + + config ATH11K_DEBUGFS_HTT_STATS + bool "Enable ath11k HTT statistics" + depends on PACKAGE_kmod-ath11k + depends on PACKAGE_MAC80211_DEBUGFS + default y + help + Say Y to enable access to the HTT statistics via debugfs. + + config ATH11K_NSS_SUPPORT + bool "Enable NSS WiFi offload" + select ATH11K_MEM_PROFILE_512M if (TARGET_qualcommax_ipq807x_DEVICE_edimax_cax1800 || \ + TARGET_qualcommax_ipq807x_DEVICE_compex_wpq873 || \ + TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4200v1 || \ + TARGET_qualcommax_ipq807x_DEVICE_redmi_ax6 || \ + TARGET_qualcommax_ipq807x_DEVICE_xiaomi_ax3600 || \ + TARGET_qualcommax_ipq807x_DEVICE_zte_mf269 ) + select ATH11K_MEM_PROFILE_256M if TARGET_qualcommax_ipq807x_DEVICE_netgear_wax218 + + config ATH11K_NSS_MESH_SUPPORT + bool "Enable NSS WiFi Mesh offload" + depends on ATH11K_NSS_SUPPORT + select PACKAGE_MAC80211_MESH + default n + + choice + prompt "Memory Profile" + depends on PACKAGE_kmod-ath11k + default ATH11K_MEM_PROFILE_1G + help + This option allows you to select the memory profile. + It should correspond to the total RAM of your board. + + config ATH11K_MEM_PROFILE_1G + bool "Use 1G memory profile" + help + This allows configuring ath11k for boards with 1GB+ memory. + + config ATH11K_MEM_PROFILE_512M + bool "Use 512MB memory profile" + help + This allows configuring ath11k for boards with 512M memory. + The default is 1GB if not selected + + config ATH11K_MEM_PROFILE_256M + bool "Use 256MB memory profile" + help + This allows configuring ath11k for boards with 256M memory. + The default is 1GB if not selected + endchoice endef define KernelPackage/ath11k-ahb diff --git a/package/kernel/mac80211/files/pbuf.uci b/package/kernel/mac80211/files/pbuf.uci new file mode 100644 index 00000000000000..4e01048e9bee0e --- /dev/null +++ b/package/kernel/mac80211/files/pbuf.uci @@ -0,0 +1,6 @@ +config general opt + # option memory_profile 'off' + option memory_profile 'auto' + # option memory_profile '1gb' + # option memory_profile '512mb' + # option memory_profile '256mb' diff --git a/package/kernel/mac80211/files/qca-nss-pbuf.init b/package/kernel/mac80211/files/qca-nss-pbuf.init new file mode 100755 index 00000000000000..c6d6b8b2238074 --- /dev/null +++ b/package/kernel/mac80211/files/qca-nss-pbuf.init @@ -0,0 +1,149 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (c) 2021 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. +# + +START=71 + +apply_sysctl() { + [ "$(sysctl -n -e dev.nss.general.redirect)" -eq 0 ] && /etc/init.d/qca-nss-ecm start + + # Running this script multiple times is useless, as extra_pbuf_core0 + # can't be changed if it is allocated, assume it's already been run. + if [ "$(sysctl -n -e dev.nss.n2hcfg.extra_pbuf_core0)" -eq 0 ]; then + logger -t ath11k_nss "$board - setting dev.nss.n2hcfg.extra_pbuf_core0=$extra_pbuf_core0" + sysctl -w dev.nss.n2hcfg.extra_pbuf_core0="$extra_pbuf_core0" > /dev/null 2> /dev/null + else + logger -t ath11k_nss "Sysctl key 'extra_pbuf_core0' already set to '""$extra_pbuf_core0""'. Skipping applying wifi nss configs" + fi + + sysctl -w dev.nss.n2hcfg.n2h_high_water_core0="$n2h_high_water_core0" > /dev/null 2>/dev/null + + logger -t ath11k_nss "$board - setting dev.nss.n2hcfg.n2h_wifi_pool_buf=$n2h_wifi_pool_buf" + sysctl -w dev.nss.n2hcfg.n2h_wifi_pool_buf="$n2h_wifi_pool_buf" + + logger -t ath11k_nss "$board - setting dev.nss.n2hcfg.n2h_high_water_core0=$n2h_high_water_core0" + sysctl -w dev.nss.n2hcfg.n2h_high_water_core0="$n2h_high_water_core0" + +} + +apply_nss_config() { + if [ ! -r /sys/module/ath11k/parameters/nss_offload ]; then + logger -t ath11k_nss "Module parameter '/sys/module/ath11k/parameters/nss_offload' does NOT exist. Skipping applying wifi nss configs" + exit 1 + fi + + enable_nss_offload=$(cat /sys/module/ath11k/parameters/nss_offload) + + if [ "$enable_nss_offload" -ne "1" ]; then + logger -t ath11k_nss -s user.warn "Module parameter 'nss_offload=0'. Skipping applying wifi nss configs" + exit 1 + fi + + [ ! -d "/proc/sys/dev/nss/rps" ] && { + logger -s -t ath11k_nss -p user.error "NSS driver not loaded or disabled! Exiting... " + exit 1 + } + + # Lock NSS clock to highest setting + sysctl -w dev.nss.clock.auto_scale=0 > /dev/null 2> /dev/null + + sysctl -w dev.nss.n2hcfg.n2h_queue_limit_core0=2048 > /dev/null 2> /dev/null + sysctl -w dev.nss.n2hcfg.n2h_queue_limit_core1=2048 > /dev/null 2> /dev/null + + sysctl -w dev.nss.rps.hash_bitmap=15 > /dev/null 2> /dev/null + + local memory_profile + if memory_profile=$(uci_get pbuf.opt.memory_profile); then + case "$memory_profile" in + 1g*|512m*|256m*) + board=$memory_profile + logger -t ath11k_nss "Using custom memory profile - $board" + ;; + off*|false*|disable*|0) + logger -s -t ath11k_nss -p user.warn "NSS pbuf option 'memory_profile=off'. Not running. Enable if you have issues connecting more than 65 clients" + exit 0 + ;; + auto) + board=$(board_name) + logger -t ath11k_nss "Setting n2hcfg values for board: $board" + ;; + *) + logger -s -t ath11k_nss -p user.error "Unknown profile $memory_profile. Choose auto, 1gb, 512mb, or 256mb" + exit 1 + ;; + esac + else + exit 0 + fi + + case "$board" in + # 1GB+ profile + arcadyan,aw1000 | \ + buffalo,wxr-5950ax12 | \ + dynalink,dl-wrx36 | \ + edgecore,eap102 | \ + linksys,mx4200v2 | \ + linksys,mx5300 | \ + netgear,rax120v2 | \ + netgear,wax620 | \ + netgear,wax630 | \ + prpl,haze | \ + qnap,301w | \ + xiaomi,ax9000 | \ + yuncore,ax880 | \ + zyxel,nbg7815 | \ + 1g*) + extra_pbuf_core0=10000000 n2h_high_water_core0=72512 n2h_wifi_pool_buf=36864 apply_sysctl + ;; + # 512MB profile + edimax,cax1800 | \ + compex,wpq873 | \ + linksys,mx4200v1 | \ + redmi,ax6 | \ + xiaomi,ax3600 | \ + zte,mf269 | \ + 512m*) + extra_pbuf_core0=3100000 n2h_high_water_core0=30624 n2h_wifi_pool_buf=8192 apply_sysctl + ;; + # 256MB profile + netgear,wax218 | \ + 256m*) + extra_pbuf_core0=3100000 n2h_high_water_core0=30258 n2h_wifi_pool_buf=4096 apply_sysctl + ;; + esac +} + +boost_performance() { + + find /sys/kernel/debug/ath11k -name stats_disable| while read -r stats_disable; do + echo 1 > "$stats_disable" + done + + ubus call iwinfo devices | jsonfilter -e "@.devices[*]"| while read -r device; do + tc qdisc replace dev "${device}" root noqueue + done + + for num in 0 1 2 3; do + echo "performance" > /sys/devices/system/cpu/cpu${num}/cpufreq/scaling_governor + done + +} + +start() { + + boost_performance + apply_nss_config + +} diff --git a/package/kernel/mac80211/patches/nss/ath10k/199-004-ath10k-fixup-nss-compile.patch b/package/kernel/mac80211/patches/nss/ath10k/199-004-ath10k-fixup-nss-compile.patch new file mode 100644 index 00000000000000..d413514144f161 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath10k/199-004-ath10k-fixup-nss-compile.patch @@ -0,0 +1,19 @@ +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -5530,7 +5530,7 @@ static int ath10k_mac_set_txbf_conf(stru + ar->wmi.vdev_param->txbf, value); + } + +-static void ath10k_update_vif_offload(struct ieee80211_hw *hw, ++static int ath10k_update_vif_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) + { + struct ath10k_vif *arvif = (void *)vif->drv_priv; +@@ -5552,6 +5552,7 @@ static void ath10k_update_vif_offload(st + ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n", + arvif->vdev_id, ret); + } ++ return ret; + } + + /* diff --git a/package/kernel/mac80211/patches/nss/ath11k/033-ath11k-fix-for-peer-memory-corruption.patch b/package/kernel/mac80211/patches/nss/ath11k/033-ath11k-fix-for-peer-memory-corruption.patch new file mode 100644 index 00000000000000..5c9c723913b09a --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/033-ath11k-fix-for-peer-memory-corruption.patch @@ -0,0 +1,48 @@ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4178,22 +4178,28 @@ static int ath11k_clear_peer_keys(struct + int ret; + int i; + u32 flags = 0; ++ struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, addr); +- spin_unlock_bh(&ab->base_lock); +- +- if (!peer) ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); + return -ENOENT; ++ } ++ for (i = 0; i < ARRAY_SIZE(keys); i++) { ++ keys[i]= peer->keys[i]; ++ peer->keys[i]= NULL; ++ } ++ spin_unlock_bh(&ab->base_lock); + +- for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { +- if (!peer->keys[i]) ++ for (i = 0; i < ARRAY_SIZE(keys); i++) { ++ if (!keys[i]) + continue; + + /* key flags are not required to delete the key */ +- ret = ath11k_install_key(arvif, peer->keys[i], ++ ret = ath11k_install_key(arvif, keys[i], + DISABLE_KEY, addr, flags); + if (ret < 0 && first_errno == 0) + first_errno = ret; +@@ -4201,10 +4207,6 @@ static int ath11k_clear_peer_keys(struct + if (ret < 0) + ath11k_warn(ab, "failed to remove peer key %d: %d\n", + i, ret); +- +- spin_lock_bh(&ab->base_lock); +- peer->keys[i] = NULL; +- spin_unlock_bh(&ab->base_lock); + } + + return first_errno; diff --git a/package/kernel/mac80211/patches/nss/ath11k/068-ath11k-add-rx-histogram-stats.patch b/package/kernel/mac80211/patches/nss/ath11k/068-ath11k-add-rx-histogram-stats.patch new file mode 100644 index 00000000000000..e4ac1ee17b0b2e --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/068-ath11k-add-rx-histogram-stats.patch @@ -0,0 +1,671 @@ +From 4635ca1f29bc5838d556b09e3c186b76a5198ddb Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 18 Aug 2023 11:43:33 +0530 +Subject: [PATCH] ath11k: add rx histogram stats + +Adding rx rate table and byte level peer rx statistics. Also, +adding a debugfs knob to reset rx stats specific to the peer. + +Signed-off-by: Manikanta Pubbisetty +--- + drivers/net/wireless/ath/ath11k/core.h | 19 ++- + drivers/net/wireless/ath/ath11k/debugfs_sta.c | 152 ++++++++++++++++-- + drivers/net/wireless/ath/ath11k/dp_rx.c | 95 +++++++++-- + drivers/net/wireless/ath/ath11k/dp_rx.h | 19 +++ + drivers/net/wireless/ath/ath11k/hal_rx.c | 85 +++++++--- + drivers/net/wireless/ath/ath11k/hal_rx.h | 21 +++ + drivers/net/wireless/ath/ath11k/hw.c | 18 +++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 8 files changed, 351 insertions(+), 59 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -41,6 +41,8 @@ + + #define ATH11K_INVALID_HW_MAC_ID 0xFF + #define ATH11K_CONNECTION_LOSS_HZ (3 * HZ) ++#define ATH11K_RX_RATE_TABLE_NUM 320 ++#define ATH11K_RX_RATE_TABLE_11AX_NUM 576 + + /* SMBIOS type containing Board Data File Name Extension */ + #define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 +@@ -376,6 +378,17 @@ struct ath11k_vif_iter { + struct ath11k_vif *arvif; + }; + ++struct ath11k_rx_peer_rate_stats { ++ u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1]; ++ u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1]; ++ u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1]; ++ u64 nss_count[HAL_RX_MAX_NSS]; ++ u64 bw_count[HAL_RX_BW_MAX]; ++ u64 gi_count[HAL_RX_GI_MAX]; ++ u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES]; ++ u64 rx_rate[ATH11K_RX_RATE_TABLE_11AX_NUM]; ++}; ++ + struct ath11k_rx_peer_stats { + u64 num_msdu; + u64 num_mpdu_fcs_ok; +@@ -387,10 +400,6 @@ struct ath11k_rx_peer_stats { + u64 non_ampdu_msdu_count; + u64 stbc_count; + u64 beamformed_count; +- u64 mcs_count[HAL_RX_MAX_MCS + 1]; +- u64 nss_count[HAL_RX_MAX_NSS]; +- u64 bw_count[HAL_RX_BW_MAX]; +- u64 gi_count[HAL_RX_GI_MAX]; + u64 coding_count[HAL_RX_SU_MU_CODING_MAX]; + u64 tid_count[IEEE80211_NUM_TIDS + 1]; + u64 pream_cnt[HAL_RX_PREAMBLE_MAX]; +@@ -398,6 +407,8 @@ struct ath11k_rx_peer_stats { + u64 rx_duration; + u64 dcm_count; + u64 ru_alloc_cnt[HAL_RX_RU_ALLOC_TYPE_MAX]; ++ struct ath11k_rx_peer_rate_stats pkt_stats; ++ struct ath11k_rx_peer_rate_stats byte_stats; + }; + + #define ATH11K_HE_MCS_NUM 12 +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -10,6 +10,7 @@ + #include "peer.h" + #include "debug.h" + #include "dp_tx.h" ++#include "dp_rx.h" + #include "debugfs_htt_stats.h" + + void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, +@@ -247,8 +248,14 @@ static ssize_t ath11k_dbg_sta_dump_rx_st + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; + int len = 0, i, retval = 0; +- const int size = 4096; ++ const int size = 4 * 4096; + char *buf; ++ int he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? 1 : 0; ++ int rate_table_len = he_rates_avail ? ATH11K_RX_RATE_TABLE_11AX_NUM : ++ ATH11K_RX_RATE_TABLE_NUM; ++ char *legacy_rate_str[] = {"1Mbps", "2Mbps", "5.5Mbps", "6Mbps", ++ "9Mbps", "11Mbps", "12Mbps", "18Mbps", ++ "24Mbps", "36 Mbps", "48Mbps", "54Mbps"}; + + if (!rx_stats) + return -ENOENT; +@@ -279,14 +286,6 @@ static ssize_t ath11k_dbg_sta_dump_rx_st + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); +- len += scnprintf(buf + len, size - len, +- "GI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", +- rx_stats->gi_count[0], rx_stats->gi_count[1], +- rx_stats->gi_count[2], rx_stats->gi_count[3]); +- len += scnprintf(buf + len, size - len, +- "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", +- rx_stats->bw_count[0], rx_stats->bw_count[1], +- rx_stats->bw_count[2], rx_stats->bw_count[3]); + len += scnprintf(buf + len, size - len, "BCC %llu LDPC %llu\n", + rx_stats->coding_count[0], rx_stats->coding_count[1]); + len += scnprintf(buf + len, size - len, +@@ -301,14 +300,96 @@ static ssize_t ath11k_dbg_sta_dump_rx_st + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); +- len += scnprintf(buf + len, size - len, "\nMCS(0-11) Legacy MCS(12):"); +- for (i = 0; i < HAL_RX_MAX_MCS + 1; i++) +- len += scnprintf(buf + len, size - len, "%llu ", rx_stats->mcs_count[i]); +- len += scnprintf(buf + len, size - len, "\nNSS(1-8):"); +- for (i = 0; i < HAL_RX_MAX_NSS; i++) +- len += scnprintf(buf + len, size - len, "%llu ", rx_stats->nss_count[i]); +- len += scnprintf(buf + len, size - len, "\nRX Duration:%llu ", ++ len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n", + rx_stats->rx_duration); ++ ++ len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n"); ++ len += scnprintf(buf + len, size - len, "\nHE packet stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->pkt_stats.he_mcs_count[i], ++ (i + 1) % 6 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nVHT packet stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->pkt_stats.vht_mcs_count[i], ++ (i + 1) % 5 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nHT packet stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->pkt_stats.ht_mcs_count[i], ++ (i + 1) % 8 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nLegacy rate packet stats:\n"); ++ for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) ++ len += scnprintf(buf + len, size - len, "%s: %llu%s", legacy_rate_str[i], ++ rx_stats->pkt_stats.legacy_count[i], ++ (i + 1) % 4 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nNSS packet stats:\n"); ++ for (i = 0; i < HAL_RX_MAX_NSS; i++) ++ len += scnprintf(buf + len, size - len, "%dx%d: %llu ", i + 1, i + 1, ++ rx_stats->pkt_stats.nss_count[i]); ++ len += scnprintf(buf + len, size - len, ++ "\n\nGI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", ++ rx_stats->pkt_stats.gi_count[0], ++ rx_stats->pkt_stats.gi_count[1], ++ rx_stats->pkt_stats.gi_count[2], ++ rx_stats->pkt_stats.gi_count[3]); ++ len += scnprintf(buf + len, size - len, ++ "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", ++ rx_stats->pkt_stats.bw_count[0], ++ rx_stats->pkt_stats.bw_count[1], ++ rx_stats->pkt_stats.bw_count[2], ++ rx_stats->pkt_stats.bw_count[3]); ++ len += scnprintf(buf + len, size - len, "\nRate Table (packets):\n"); ++ for (i = 0; i < rate_table_len; i++) ++ len += scnprintf(buf + len, size - len, "%10llu%s", ++ rx_stats->pkt_stats.rx_rate[i], ++ (i + 1) % (he_rates_avail ? 12 : 8) ? "\t" : "\n"); ++ ++ len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n"); ++ len += scnprintf(buf + len, size - len, "\nHE byte stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->byte_stats.he_mcs_count[i], ++ (i + 1) % 6 ? "\t" : "\n"); ++ ++ len += scnprintf(buf + len, size - len, "\nVHT byte stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->byte_stats.vht_mcs_count[i], ++ (i + 1) % 5 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nHT byte stats:\n"); ++ for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) ++ len += scnprintf(buf + len, size - len, "MCS %d: %llu%s", i, ++ rx_stats->byte_stats.ht_mcs_count[i], ++ (i + 1) % 8 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nLegacy rate byte stats:\n"); ++ for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) ++ len += scnprintf(buf + len, size - len, "%s: %llu%s", legacy_rate_str[i], ++ rx_stats->byte_stats.legacy_count[i], ++ (i + 1) % 4 ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, "\nNSS byte stats:\n"); ++ for (i = 0; i < HAL_RX_MAX_NSS; i++) ++ len += scnprintf(buf + len, size - len, "%dx%d: %llu ", i + 1, i + 1, ++ rx_stats->byte_stats.nss_count[i]); ++ len += scnprintf(buf + len, size - len, ++ "\n\nGI: 0.8us %llu 0.4us %llu 1.6us %llu 3.2us %llu\n", ++ rx_stats->byte_stats.gi_count[0], ++ rx_stats->byte_stats.gi_count[1], ++ rx_stats->byte_stats.gi_count[2], ++ rx_stats->byte_stats.gi_count[3]); ++ len += scnprintf(buf + len, size - len, ++ "BW: 20Mhz %llu 40Mhz %llu 80Mhz %llu 160Mhz %llu\n", ++ rx_stats->byte_stats.bw_count[0], ++ rx_stats->byte_stats.bw_count[1], ++ rx_stats->byte_stats.bw_count[2], ++ rx_stats->byte_stats.bw_count[3]); ++ len += scnprintf(buf + len, size - len, "\nRate Table (bytes):\n"); ++ for (i = 0; i < rate_table_len; i++) ++ len += scnprintf(buf + len, size - len, "%10llu%s", ++ rx_stats->byte_stats.rx_rate[i], ++ (i + 1) % (he_rates_avail ? 12 : 8) ? "\t" : "\n"); ++ + len += scnprintf(buf + len, size - len, + "\nDCM: %llu\nRU: 26 %llu 52: %llu 106: %llu 242: %llu 484: %llu 996: %llu\n", + rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], +@@ -847,6 +928,40 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++static ssize_t ath11k_dbg_sta_reset_rx_stats(struct file *file, ++ const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ieee80211_sta *sta = file->private_data; ++ struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv; ++ struct ath11k *ar = arsta->arvif->ar; ++ int ret, reset; ++ ++ if (!arsta->rx_stats) ++ return -ENOENT; ++ ++ ret = kstrtoint_from_user(buf, count, 0, &reset); ++ if (ret) ++ return ret; ++ ++ if (!reset || reset > 1) ++ return -EINVAL; ++ ++ spin_lock_bh(&ar->ab->base_lock); ++ memset(arsta->rx_stats, 0, sizeof(*arsta->rx_stats)); ++ spin_unlock_bh(&ar->ab->base_lock); ++ ++ ret = count; ++ return ret; ++} ++ ++static const struct file_operations fops_reset_rx_stats = { ++ .write = ath11k_dbg_sta_reset_rx_stats, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ + void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) + { +@@ -855,9 +970,12 @@ void ath11k_debugfs_sta_op_add(struct ie + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) + debugfs_create_file("tx_stats", 0400, dir, sta, + &fops_tx_stats); +- if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) { + debugfs_create_file("rx_stats", 0400, dir, sta, + &fops_rx_stats); ++ debugfs_create_file("reset_rx_stats", 0600, dir, sta, ++ &fops_reset_rx_stats); ++ } + + debugfs_create_file("htt_peer_stats", 0400, dir, sta, + &fops_htt_peer_stats); +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2762,10 +2762,43 @@ exit: + return total_msdu_reaped; + } + ++static void ++ath11k_dp_rx_update_peer_rate_table_stats(struct ath11k_rx_peer_stats *rx_stats, ++ struct hal_rx_mon_ppdu_info *ppdu_info, ++ u32 num_msdu) ++{ ++ u32 rate_idx = 0; ++ u32 mcs_idx = ppdu_info->mcs; ++ u32 nss_idx = ppdu_info->nss - 1; ++ u32 bw_idx = ppdu_info->bw; ++ u32 gi_idx = ppdu_info->gi; ++ ++ if ((mcs_idx > HAL_RX_MAX_MCS_HE) || (nss_idx >= HAL_RX_MAX_NSS) || ++ (bw_idx >= HAL_RX_BW_MAX) || (gi_idx >= HAL_RX_GI_MAX)) { ++ return; ++ } ++ ++ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N || ++ ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC) { ++ rate_idx = mcs_idx * 8 + 8 * 10 * nss_idx; ++ rate_idx += bw_idx * 2 + gi_idx; ++ } else if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX) { ++ gi_idx = ath11k_he_gi_to_nl80211_he_gi(ppdu_info->gi); ++ rate_idx = mcs_idx * 12 + 12 * 12 * nss_idx; ++ rate_idx += bw_idx * 3 + gi_idx; ++ } else { ++ return; ++ } ++ ++ rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu; ++ rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; ++} ++ + static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, + struct hal_rx_mon_ppdu_info *ppdu_info) + { + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; ++ struct ath11k *ar = arsta->arvif->ar; + u32 num_msdu; + int i; + +@@ -2775,6 +2808,8 @@ static void ath11k_dp_rx_update_peer_sta + arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); + ++ if (!ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ return; + num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; + +@@ -2791,18 +2826,6 @@ static void ath11k_dp_rx_update_peer_sta + ppdu_info->tid = IEEE80211_NUM_TIDS; + } + +- if (ppdu_info->nss > 0 && ppdu_info->nss <= HAL_RX_MAX_NSS) +- rx_stats->nss_count[ppdu_info->nss - 1] += num_msdu; +- +- if (ppdu_info->mcs <= HAL_RX_MAX_MCS) +- rx_stats->mcs_count[ppdu_info->mcs] += num_msdu; +- +- if (ppdu_info->gi < HAL_RX_GI_MAX) +- rx_stats->gi_count[ppdu_info->gi] += num_msdu; +- +- if (ppdu_info->bw < HAL_RX_BW_MAX) +- rx_stats->bw_count[ppdu_info->bw] += num_msdu; +- + if (ppdu_info->ldpc < HAL_RX_SU_MU_CODING_MAX) + rx_stats->coding_count[ppdu_info->ldpc] += num_msdu; + +@@ -2831,8 +2854,6 @@ static void ath11k_dp_rx_update_peer_sta + rx_stats->dcm_count += ppdu_info->dcm; + rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; + +- arsta->rssi_comb = ppdu_info->rssi_comb; +- + BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > + ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); + +@@ -2841,6 +2862,52 @@ static void ath11k_dp_rx_update_peer_sta + + rx_stats->rx_duration += ppdu_info->rx_duration; + arsta->rx_duration = rx_stats->rx_duration; ++ ++ if (ppdu_info->nss > 0 && ppdu_info->nss <= HAL_RX_MAX_NSS) { ++ rx_stats->pkt_stats.nss_count[ppdu_info->nss - 1] += num_msdu; ++ rx_stats->byte_stats.nss_count[ppdu_info->nss - 1] += ppdu_info->mpdu_len; ++ } ++ ++ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N && ++ ppdu_info->mcs <= HAL_RX_MAX_MCS_HT) { ++ rx_stats->pkt_stats.ht_mcs_count[ppdu_info->mcs] += num_msdu; ++ rx_stats->byte_stats.ht_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; ++ /* To fit into rate table for HT packets */ ++ ppdu_info->mcs = ppdu_info->mcs % 8; ++ } ++ ++ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC && ++ ppdu_info->mcs <= HAL_RX_MAX_MCS_VHT) { ++ rx_stats->pkt_stats.vht_mcs_count[ppdu_info->mcs] += num_msdu; ++ rx_stats->byte_stats.vht_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; ++ } ++ ++ if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX && ++ ppdu_info->mcs <= HAL_RX_MAX_MCS_HE) { ++ rx_stats->pkt_stats.he_mcs_count[ppdu_info->mcs] += num_msdu; ++ rx_stats->byte_stats.he_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; ++ } ++ ++ ++ if ((ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || ++ ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) && ++ ppdu_info->rate < HAL_RX_LEGACY_RATE_INVALID) { ++ rx_stats->pkt_stats.legacy_count[ppdu_info->rate] += num_msdu; ++ rx_stats->byte_stats.legacy_count[ppdu_info->rate] += ppdu_info->mpdu_len; ++ } ++ ++ if (ppdu_info->gi < HAL_RX_GI_MAX) { ++ rx_stats->pkt_stats.gi_count[ppdu_info->gi] += num_msdu; ++ rx_stats->byte_stats.gi_count[ppdu_info->gi] += ppdu_info->mpdu_len; ++ } ++ ++ if (ppdu_info->bw < HAL_RX_BW_MAX) { ++ rx_stats->pkt_stats.bw_count[ppdu_info->bw] += num_msdu; ++ rx_stats->byte_stats.bw_count[ppdu_info->bw] += ppdu_info->mpdu_len; ++ } ++ ++ ath11k_dp_rx_update_peer_rate_table_stats(rx_stats, ppdu_info, num_msdu); ++ + } + + static struct sk_buff *ath11k_dp_rx_alloc_mon_status_buf(struct ath11k_base *ab, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -41,6 +41,25 @@ struct ath11k_dp_rfc1042_hdr { + __be16 snap_type; + } __packed; + ++static inline u32 ath11k_he_gi_to_nl80211_he_gi(u8 sgi) ++{ ++ u32 ret = 0; ++ ++ switch (sgi) { ++ case RX_MSDU_START_SGI_0_8_US: ++ ret = NL80211_RATE_INFO_HE_GI_0_8; ++ break; ++ case RX_MSDU_START_SGI_1_6_US: ++ ret = NL80211_RATE_INFO_HE_GI_1_6; ++ break; ++ case RX_MSDU_START_SGI_3_2_US: ++ ret = NL80211_RATE_INFO_HE_GI_3_2; ++ break; ++ } ++ ++ return ret; ++} ++ + int ath11k_dp_rx_ampdu_start(struct ath11k *ar, + struct ieee80211_ampdu_params *params); + int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -975,44 +975,78 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->is_stbc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_STBC, + info1); + ppdu_info->ldpc = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING, info1); +- ppdu_info->gi = info1 & HAL_RX_HT_SIG_INFO_INFO1_GI; +- +- switch (ppdu_info->mcs) { +- case 0 ... 7: +- ppdu_info->nss = 1; +- break; +- case 8 ... 15: +- ppdu_info->nss = 2; +- break; +- case 16 ... 23: +- ppdu_info->nss = 3; +- break; +- case 24 ... 31: +- ppdu_info->nss = 4; +- break; +- } +- +- if (ppdu_info->nss > 1) +- ppdu_info->mcs = ppdu_info->mcs % 8; +- ++ ppdu_info->gi = FIELD_GET(HAL_RX_HT_SIG_INFO_INFO1_GI, info1); ++ ppdu_info->nss = (ppdu_info->mcs >> 3) + 1; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_B: { + struct hal_rx_lsig_b_info *lsigb = + (struct hal_rx_lsig_b_info *)tlv_data; ++ u8 rate; ++ ++ rate = FIELD_GET(HAL_RX_LSIG_B_INFO_INFO0_RATE, ++ __le32_to_cpu(lsigb->info0)); + +- ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_B_INFO_INFO0_RATE, +- __le32_to_cpu(lsigb->info0)); ++ switch (rate) { ++ case 1: ++ rate = HAL_RX_LEGACY_RATE_1_MBPS; ++ break; ++ case 2: ++ case 5: ++ rate = HAL_RX_LEGACY_RATE_2_MBPS; ++ break; ++ case 3: ++ case 6: ++ rate = HAL_RX_LEGACY_RATE_5_5_MBPS; ++ break; ++ case 4: ++ case 7: ++ rate = HAL_RX_LEGACY_RATE_11_MBPS; ++ break; ++ default: ++ rate = HAL_RX_LEGACY_RATE_INVALID; ++ } ++ ppdu_info->rate = rate; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } + case HAL_PHYRX_L_SIG_A: { + struct hal_rx_lsig_a_info *lsiga = + (struct hal_rx_lsig_a_info *)tlv_data; ++ u8 rate; + +- ppdu_info->rate = FIELD_GET(HAL_RX_LSIG_A_INFO_INFO0_RATE, +- __le32_to_cpu(lsiga->info0)); ++ rate = FIELD_GET(HAL_RX_LSIG_A_INFO_INFO0_RATE, ++ __le32_to_cpu(lsiga->info0)); ++ switch (rate) { ++ case 8: ++ rate = HAL_RX_LEGACY_RATE_48_MBPS; ++ break; ++ case 9: ++ rate = HAL_RX_LEGACY_RATE_24_MBPS; ++ break; ++ case 10: ++ rate = HAL_RX_LEGACY_RATE_12_MBPS; ++ break; ++ case 11: ++ rate = HAL_RX_LEGACY_RATE_6_MBPS; ++ break; ++ case 12: ++ rate = HAL_RX_LEGACY_RATE_54_MBPS; ++ break; ++ case 13: ++ rate = HAL_RX_LEGACY_RATE_36_MBPS; ++ break; ++ case 14: ++ rate = HAL_RX_LEGACY_RATE_18_MBPS; ++ break; ++ case 15: ++ rate = HAL_RX_LEGACY_RATE_9_MBPS; ++ break; ++ default: ++ rate = HAL_RX_LEGACY_RATE_INVALID; ++ } ++ ppdu_info->rate = rate; + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + } +@@ -1470,6 +1504,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); + if (peer_id) + ppdu_info->peer_id = peer_id; ++ ++ ppdu_info->mpdu_len += ab->hw_params.hw_ops->rx_desc_get_hal_mpdu_len(mpdu_info); ++ + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -19,7 +19,11 @@ struct hal_rx_wbm_rel_info { + #define VHT_SIG_SU_NSS_MASK 0x7 + + #define HAL_RX_MAX_MCS 12 ++#define HAL_RX_MAX_MCS_HT 31 ++#define HAL_RX_MAX_MCS_VHT 9 ++#define HAL_RX_MAX_MCS_HE 11 + #define HAL_RX_MAX_NSS 8 ++#define HAL_RX_MAX_NUM_LEGACY_RATES 12 + + struct hal_rx_mon_status_tlv_hdr { + u32 hdr; +@@ -103,6 +107,22 @@ struct hal_rx_user_status { + u32 mpdu_err_byte_count; + }; + ++enum hal_rx_legacy_rate { ++ HAL_RX_LEGACY_RATE_1_MBPS, ++ HAL_RX_LEGACY_RATE_2_MBPS, ++ HAL_RX_LEGACY_RATE_5_5_MBPS, ++ HAL_RX_LEGACY_RATE_6_MBPS, ++ HAL_RX_LEGACY_RATE_9_MBPS, ++ HAL_RX_LEGACY_RATE_11_MBPS, ++ HAL_RX_LEGACY_RATE_12_MBPS, ++ HAL_RX_LEGACY_RATE_18_MBPS, ++ HAL_RX_LEGACY_RATE_24_MBPS, ++ HAL_RX_LEGACY_RATE_36_MBPS, ++ HAL_RX_LEGACY_RATE_48_MBPS, ++ HAL_RX_LEGACY_RATE_54_MBPS, ++ HAL_RX_LEGACY_RATE_INVALID, ++}; ++ + #define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE + #define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE + #define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE +@@ -127,6 +147,7 @@ struct hal_rx_mon_ppdu_info { + u32 num_mpdu_fcs_ok; + u32 num_mpdu_fcs_err; + u32 preamble_type; ++ u32 mpdu_len; + u16 chan_num; + u16 tcp_msdu_count; + u16 tcp_ack_msdu_count; +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -900,6 +900,17 @@ static u32 ath11k_hw_wcn6750_get_tcl_rin + return skb_get_hash(skb); + } + ++static u32 ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len(struct hal_rx_mpdu_info *mpdu_info) ++{ ++ return FIELD_GET(HAL_RX_MPDU_INFO_INFO1_MPDU_LEN, ++ __le32_to_cpu(mpdu_info->u.ipq8074.info1)); ++} ++ ++static u32 ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len(struct hal_rx_mpdu_info *mpdu_info) ++{ ++ return FIELD_GET(HAL_RX_MPDU_INFO_INFO1_MPDU_LEN, ++ __le32_to_cpu(mpdu_info->u.qcn9074.info1)); ++} + const struct ath11k_hw_ops ipq8074_ops = { + .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = ath11k_init_wmi_config_ipq8074, +@@ -938,6 +949,7 @@ const struct ath11k_hw_ops ipq8074_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, + }; + + const struct ath11k_hw_ops ipq6018_ops = { +@@ -978,6 +990,7 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, + }; + + const struct ath11k_hw_ops qca6390_ops = { +@@ -1018,6 +1031,7 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_ipq8074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, + }; + + const struct ath11k_hw_ops qcn9074_ops = { +@@ -1058,6 +1072,7 @@ const struct ath11k_hw_ops qcn9074_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len, + }; + + const struct ath11k_hw_ops wcn6855_ops = { +@@ -1098,6 +1113,7 @@ const struct ath11k_hw_ops wcn6855_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_wcn6855_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_wcn6855_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, + }; + + const struct ath11k_hw_ops wcn6750_ops = { +@@ -1179,6 +1195,7 @@ const struct ath11k_hw_ops ipq5018_ops = + .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, ++ .rx_desc_get_hal_mpdu_len = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len, + }; + + #define ATH11K_TX_RING_MASK_0 BIT(0) +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -269,6 +269,7 @@ struct ath11k_hw_ops { + bool (*rx_desc_mac_addr2_valid)(struct hal_rx_desc *desc); + u8* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc); + u32 (*get_ring_selector)(struct sk_buff *skb); ++ u32 (*rx_desc_get_hal_mpdu_len)(struct hal_rx_mpdu_info *mpdu_info); + }; + + extern const struct ath11k_hw_ops ipq8074_ops; diff --git a/package/kernel/mac80211/patches/nss/ath11k/069-ath11k-add-HE-stats-in-peer-stats.patch b/package/kernel/mac80211/patches/nss/ath11k/069-ath11k-add-HE-stats-in-peer-stats.patch new file mode 100644 index 00000000000000..5c29297c3363f8 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/069-ath11k-add-HE-stats-in-peer-stats.patch @@ -0,0 +1,779 @@ +From e4f16128c53b48f166301085cecc23f77bf3ff8e Mon Sep 17 00:00:00 2001 +From: Miles Hu +Date: Fri, 11 Oct 2019 19:24:06 -0700 +Subject: [PATCH] ath11k: add HE stats in peer stats packet counters for MIMO + and OFDMA + +Signed-off-by: Miles Hu +--- + drivers/net/wireless/ath/ath11k/core.h | 23 ++++- + drivers/net/wireless/ath/ath11k/debugfs_sta.c | 127 +++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/dp.h | 21 ++++- + drivers/net/wireless/ath/ath11k/dp_rx.c | 17 +++- + drivers/net/wireless/ath/ath11k/rx_desc.h | 5 + + 5 files changed, 185 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -29,6 +29,7 @@ + #include "dbring.h" + #include "spectral.h" + #include "wow.h" ++#include "rx_desc.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -469,6 +470,8 @@ struct ath11k_htt_data_stats { + u64 bw[ATH11K_COUNTER_TYPE_MAX][ATH11K_BW_NUM]; + u64 nss[ATH11K_COUNTER_TYPE_MAX][ATH11K_NSS_NUM]; + u64 gi[ATH11K_COUNTER_TYPE_MAX][ATH11K_GI_NUM]; ++ u64 transmit_type[ATH11K_COUNTER_TYPE_MAX][HAL_RX_RECEPTION_TYPE_MAX]; ++ u64 ru_loc[ATH11K_COUNTER_TYPE_MAX][HAL_RX_RU_ALLOC_TYPE_MAX]; + }; + + struct ath11k_htt_tx_stats { +@@ -476,6 +479,9 @@ struct ath11k_htt_tx_stats { + u64 tx_duration; + u64 ba_fails; + u64 ack_fails; ++ u16 ru_start; ++ u16 ru_tones; ++ u32 mu_group[MAX_MU_GROUP_ID]; + }; + + struct ath11k_per_ppdu_tx_stats { +@@ -592,11 +598,16 @@ struct ath11k_per_peer_tx_stats { + u32 succ_bytes; + u32 retry_bytes; + u32 failed_bytes; ++ u32 duration; + u16 succ_pkts; + u16 retry_pkts; + u16 failed_pkts; +- u32 duration; ++ u16 ru_start; ++ u16 ru_tones; + u8 ba_fails; ++ u8 ppdu_type; ++ u32 mu_grpid; ++ u32 mu_pos; + bool is_ampdu; + }; + +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -13,13 +13,39 @@ + #include "dp_rx.h" + #include "debugfs_htt_stats.h" + ++static inline u32 ath11k_he_tones_in_ru_to_nl80211_he_ru_alloc(u16 ru_tones) ++{ ++ u32 ret = 0; ++ switch (ru_tones) { ++ case 26: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_26; ++ break; ++ case 52: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_52; ++ break; ++ case 106: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_106; ++ break; ++ case 242: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_242; ++ break; ++ case 484: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_484; ++ break; ++ case 996: ++ ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; ++ break; ++ } ++ return ret; ++} ++ + void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta, + struct ath11k_per_peer_tx_stats *peer_stats, + u8 legacy_rate_idx) + { + struct rate_info *txrate = &arsta->txrate; + struct ath11k_htt_tx_stats *tx_stats; +- int gi, mcs, bw, nss; ++ int gi, mcs, bw, nss, ru_type, ppdu_type; + + if (!arsta->tx_stats) + return; +@@ -64,6 +90,43 @@ void ath11k_debugfs_sta_add_tx_stats(str + STATS_OP_FMT(RETRY).legacy[1][mcs] += peer_stats->retry_pkts; + } + ++ ppdu_type = peer_stats->ppdu_type; ++ if ((ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA || ++ ppdu_type == HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO_OFDMA) && ++ (txrate->flags & RATE_INFO_FLAGS_HE_MCS)){ ++ ru_type = peer_stats->ru_tones; ++ ++ if (ru_type <= NL80211_RATE_INFO_HE_RU_ALLOC_996) { ++ STATS_OP_FMT(SUCC).ru_loc[0][ru_type] += peer_stats->succ_bytes; ++ STATS_OP_FMT(SUCC).ru_loc[1][ru_type] += peer_stats->succ_pkts; ++ STATS_OP_FMT(FAIL).ru_loc[0][ru_type] += peer_stats->failed_bytes; ++ STATS_OP_FMT(FAIL).ru_loc[1][ru_type] += peer_stats->failed_pkts; ++ STATS_OP_FMT(RETRY).ru_loc[0][ru_type] += peer_stats->retry_bytes; ++ STATS_OP_FMT(RETRY).ru_loc[1][ru_type] += peer_stats->retry_pkts; ++ if (peer_stats->is_ampdu) { ++ STATS_OP_FMT(AMPDU).ru_loc[0][ru_type] += ++ peer_stats->succ_bytes + peer_stats->retry_bytes; ++ STATS_OP_FMT(AMPDU).ru_loc[1][ru_type] += ++ peer_stats->succ_pkts + peer_stats->retry_pkts; ++ } ++ } ++ } ++ ++ if (ppdu_type < HTT_PPDU_STATS_PPDU_TYPE_MAX) { ++ STATS_OP_FMT(SUCC).transmit_type[0][ppdu_type] += peer_stats->succ_bytes; ++ STATS_OP_FMT(SUCC).transmit_type[1][ppdu_type] += peer_stats->succ_pkts; ++ STATS_OP_FMT(FAIL).transmit_type[0][ppdu_type] += peer_stats->failed_bytes; ++ STATS_OP_FMT(FAIL).transmit_type[1][ppdu_type] += peer_stats->failed_pkts; ++ STATS_OP_FMT(RETRY).transmit_type[0][ppdu_type] += peer_stats->retry_bytes; ++ STATS_OP_FMT(RETRY).transmit_type[1][ppdu_type] += peer_stats->retry_pkts; ++ if (peer_stats->is_ampdu) { ++ STATS_OP_FMT(AMPDU).transmit_type[0][ppdu_type] += ++ peer_stats->succ_bytes + peer_stats->retry_bytes; ++ STATS_OP_FMT(AMPDU).transmit_type[1][ppdu_type] += ++ peer_stats->succ_pkts + peer_stats->retry_pkts; ++ } ++ } ++ + if (peer_stats->is_ampdu) { + tx_stats->ba_fails += peer_stats->ba_fails; + +@@ -124,6 +187,17 @@ void ath11k_debugfs_sta_add_tx_stats(str + STATS_OP_FMT(RETRY).gi[1][gi] += peer_stats->retry_pkts; + + tx_stats->tx_duration += peer_stats->duration; ++ ++ tx_stats->ru_start = peer_stats->ru_start; ++ tx_stats->ru_tones = peer_stats->ru_tones; ++ ++ if (peer_stats->mu_grpid <= MAX_MU_GROUP_ID && ++ peer_stats->ppdu_type != HTT_PPDU_STATS_PPDU_TYPE_SU) { ++ if (peer_stats->mu_grpid & (MAX_MU_GROUP_ID - 1)) ++ tx_stats->mu_group[peer_stats->mu_grpid] = ++ (peer_stats->mu_pos + 1); ++ } ++ + } + + void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar, +@@ -140,12 +214,13 @@ static ssize_t ath11k_dbg_sta_dump_tx_st + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_htt_data_stats *stats; +- static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"succ", "fail", ++ static const char *str_name[ATH11K_STATS_TYPE_MAX] = {"success", "fail", + "retry", "ampdu"}; + static const char *str[ATH11K_COUNTER_TYPE_MAX] = {"bytes", "packets"}; + int len = 0, i, j, k, retval = 0; + const int size = 2 * 4096; +- char *buf; ++ char *buf, mu_group_id[MAX_MU_GROUP_LENGTH] = {0}; ++ u32 index; + + if (!arsta->tx_stats) + return -ENOENT; +@@ -163,45 +238,46 @@ static ssize_t ath11k_dbg_sta_dump_tx_st + len += scnprintf(buf + len, size - len, "%s_%s\n", + str_name[k], + str[j]); ++ len += scnprintf(buf + len, size - len, "==========\n"); + len += scnprintf(buf + len, size - len, +- " HE MCS %s\n", ++ " HE MCS %s\n\t", + str[j]); + for (i = 0; i < ATH11K_HE_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, +- " %llu ", ++ "%llu ", + stats->he[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, +- " VHT MCS %s\n", ++ " VHT MCS %s\n\t", + str[j]); + for (i = 0; i < ATH11K_VHT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, +- " %llu ", ++ "%llu ", + stats->vht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); +- len += scnprintf(buf + len, size - len, " HT MCS %s\n", ++ len += scnprintf(buf + len, size - len, " HT MCS %s\n\t", + str[j]); + for (i = 0; i < ATH11K_HT_MCS_NUM; i++) + len += scnprintf(buf + len, size - len, +- " %llu ", stats->ht[j][i]); ++ "%llu ", stats->ht[j][i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " BW %s (20,40,80,160 MHz)\n", str[j]); + len += scnprintf(buf + len, size - len, +- " %llu %llu %llu %llu\n", ++ "\t%llu %llu %llu %llu\n", + stats->bw[j][0], stats->bw[j][1], + stats->bw[j][2], stats->bw[j][3]); + len += scnprintf(buf + len, size - len, + " NSS %s (1x1,2x2,3x3,4x4)\n", str[j]); + len += scnprintf(buf + len, size - len, +- " %llu %llu %llu %llu\n", ++ "\t%llu %llu %llu %llu\n", + stats->nss[j][0], stats->nss[j][1], + stats->nss[j][2], stats->nss[j][3]); + len += scnprintf(buf + len, size - len, + " GI %s (0.4us,0.8us,1.6us,3.2us)\n", + str[j]); + len += scnprintf(buf + len, size - len, +- " %llu %llu %llu %llu\n", ++ "\t%llu %llu %llu %llu\n", + stats->gi[j][0], stats->gi[j][1], + stats->gi[j][2], stats->gi[j][3]); + len += scnprintf(buf + len, size - len, +@@ -210,10 +286,68 @@ static ssize_t ath11k_dbg_sta_dump_tx_st + for (i = 0; i < ATH11K_LEGACY_NUM; i++) + len += scnprintf(buf + len, size - len, "%llu ", + stats->legacy[j][i]); +- len += scnprintf(buf + len, size - len, "\n"); ++ ++ len += scnprintf(buf + len, size - len, "\n ru %s: \n", str[j]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 26: %llu\n", stats->ru_loc[j][0]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 52: %llu \n", stats->ru_loc[j][1]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 106: %llu \n", stats->ru_loc[j][2]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 242: %llu \n", stats->ru_loc[j][3]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 484: %llu \n", stats->ru_loc[j][4]); ++ len += scnprintf(buf + len, size - len, ++ "\tru 996: %llu \n", stats->ru_loc[j][5]); ++ ++ len += scnprintf(buf + len, size - len, ++ " ppdu type %s: \n", str[j]); ++ if (k == ATH11K_STATS_TYPE_FAIL || ++ k == ATH11K_STATS_TYPE_RETRY) { ++ len += scnprintf(buf + len, size - len, ++ "\tSU/MIMO: %llu\n", ++ stats->transmit_type[j][0]); ++ len += scnprintf(buf + len, size - len, ++ "\tOFDMA/OFDMA_MIMO: %llu\n", ++ stats->transmit_type[j][2]); ++ } else { ++ len += scnprintf(buf + len, size - len, ++ "\tSU: %llu\n", ++ stats->transmit_type[j][0]); ++ len += scnprintf(buf + len, size - len, ++ "\tMIMO: %llu\n", ++ stats->transmit_type[j][1]); ++ len += scnprintf(buf + len, size - len, ++ "\tOFDMA: %llu\n", ++ stats->transmit_type[j][2]); ++ len += scnprintf(buf + len, size - len, ++ "\tOFDMA_MIMO: %llu\n", ++ stats->transmit_type[j][3]); ++ } + } + } + ++ len += scnprintf(buf + len, size - len, "\n"); ++ ++ for (i = 0; i < MAX_MU_GROUP_ID;) { ++ index = 0; ++ for (j = 0; j < MAX_MU_GROUP_SHOW && i < MAX_MU_GROUP_ID; ++ j++) { ++ index += snprintf(&mu_group_id[index], ++ MAX_MU_GROUP_LENGTH - index, ++ " %d", ++ arsta->tx_stats->mu_group[i]); ++ i++; ++ } ++ len += scnprintf(buf + len, size - len, ++ "User position list for GID %02d->%d: [%s]\n", ++ i - MAX_MU_GROUP_SHOW, i - 1, mu_group_id); ++ } ++ len += scnprintf(buf + len, size - len, ++ "\nLast Packet RU index [%d], Size [%d]\n", ++ arsta->tx_stats->ru_start, arsta->tx_stats->ru_tones); ++ + len += scnprintf(buf + len, size - len, + "\nTX duration\n %llu usecs\n", + arsta->tx_stats->tx_duration); +@@ -221,6 +355,7 @@ static ssize_t ath11k_dbg_sta_dump_tx_st + "BA fails\n %llu\n", arsta->tx_stats->ba_fails); + len += scnprintf(buf + len, size - len, + "ack fails\n %llu\n", arsta->tx_stats->ack_fails); ++ + spin_unlock_bh(&ar->data_lock); + + if (len > size) +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -595,6 +595,45 @@ enum htt_ppdu_stats_tag_type { + BIT(HTT_PPDU_STATS_TAG_TX_MGMTCTRL_PAYLOAD) | \ + HTT_PPDU_STATS_TAG_DEFAULT) + ++#define HTT_STATS_FRAMECTRL_TYPE_MASK 0x0C ++#define HTT_STATS_GET_FRAME_CTRL_TYPE(_val) \ ++ (((_val) & HTT_STATS_FRAMECTRL_TYPE_MASK) >> 2) ++#define HTT_STATS_FRAME_CTRL_TYPE_MGMT 0x0 ++#define HTT_STATS_FRAME_CTRL_TYPE_CTRL 0x1 ++#define HTT_STATS_FRAME_CTRL_TYPE_DATA 0x2 ++#define HTT_STATS_FRAME_CTRL_TYPE_RESV 0x3 ++ ++enum htt_stats_frametype { ++ HTT_STATS_FTYPE_SGEN_NDPA = 0, ++ HTT_STATS_FTYPE_SGEN_NDP, ++ HTT_STATS_FTYPE_SGEN_BRP, ++ HTT_STATS_FTYPE_SGEN_BAR, ++ HTT_STATS_FTYPE_SGEN_RTS, ++ HTT_STATS_FTYPE_SGEN_CTS, ++ HTT_STATS_FTYPE_SGEN_CFEND, ++ HTT_STATS_FTYPE_SGEN_AX_NDPA, ++ HTT_STATS_FTYPE_SGEN_AX_NDP, ++ HTT_STATS_FTYPE_SGEN_MU_TRIG, ++ HTT_STATS_FTYPE_SGEN_MU_BAR, ++ HTT_STATS_FTYPE_SGEN_MU_BRP, ++ HTT_STATS_FTYPE_SGEN_MU_RTS, ++ HTT_STATS_FTYPE_SGEN_MU_BSR, ++ HTT_STATS_FTYPE_SGEN_UL_BSR, ++ HTT_STATS_FTYPE_SGEN_UL_BSR_TRIGGER = HTT_STATS_FTYPE_SGEN_UL_BSR, ++ HTT_STATS_FTYPE_TIDQ_DATA_SU, ++ HTT_STATS_FTYPE_TIDQ_DATA_MU, ++ HTT_STATS_FTYPE_SGEN_UL_BSR_RESP, ++ HTT_STATS_FTYPE_SGEN_QOS_NULL, ++ HTT_STATS_FTYPE_MAX, ++}; ++ ++enum htt_stats_internal_ppdu_frametype { ++ HTT_STATS_PPDU_FTYPE_CTRL, ++ HTT_STATS_PPDU_FTYPE_DATA, ++ HTT_STATS_PPDU_FTYPE_BAR, ++ HTT_STATS_PPDU_FTYPE_MAX ++}; ++ + /* HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG Message + * + * details: +@@ -1234,6 +1273,19 @@ enum htt_ppdu_stats_gi { + #define HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M GENMASK(3, 0) + #define HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M GENMASK(11, 4) + ++enum HTT_PPDU_STATS_PPDU_TYPE { ++ HTT_PPDU_STATS_PPDU_TYPE_SU, ++ HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO, ++ HTT_PPDU_STATS_PPDU_TYPE_MU_OFDMA, ++ HTT_PPDU_STATS_PPDU_TYPE_MU_MIMO_OFDMA, ++ HTT_PPDU_STATS_PPDU_TYPE_UL_TRIG, ++ HTT_PPDU_STATS_PPDU_TYPE_BURST_BCN, ++ HTT_PPDU_STATS_PPDU_TYPE_UL_BSR_RESP, ++ HTT_PPDU_STATS_PPDU_TYPE_UL_BSR_TRIG, ++ HTT_PPDU_STATS_PPDU_TYPE_UL_RESP, ++ HTT_PPDU_STATS_PPDU_TYPE_MAX ++}; ++ + #define HTT_PPDU_STATS_USER_RATE_INFO1_RESP_TYPE_VALD_M BIT(0) + #define HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M GENMASK(5, 1) + +@@ -1261,6 +1313,12 @@ enum htt_ppdu_stats_gi { + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_GI_M, _val) + #define HTT_USR_RATE_DCM(_val) \ + FIELD_GET(HTT_PPDU_STATS_USER_RATE_FLAGS_DCM_M, _val) ++#define HTT_USR_RATE_PPDU_TYPE(_val) \ ++ FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO1_PPDU_TYPE_M, _val) ++#define HTT_USR_RATE_MU_GRPID(_val) \ ++ FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO0_MU_GROUP_ID_M, _val) ++#define HTT_USR_RATE_USR_POS(_val) \ ++ FIELD_GET(HTT_PPDU_STATS_USER_RATE_INFO0_USER_POS_M, _val) + + #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_LTF_SIZE_M GENMASK(1, 0) + #define HTT_PPDU_STATS_USER_RATE_RESP_FLAGS_STBC_M BIT(2) +@@ -1364,6 +1422,21 @@ struct htt_ppdu_stats_usr_cmpltn_ack_ba_ + u32 success_bytes; + } __packed; + ++#define HTT_PPDU_STATS_USR_CMN_FLAG_DELAYBA BIT(14) ++#define HTT_PPDU_STATS_USR_CMN_HDR_SW_PEERID GENMASK(31, 16) ++#define HTT_PPDU_STATS_USR_CMN_CTL_FRM_CTRL GENMASK(15, 0) ++ ++struct htt_ppdu_stats_user_common { ++ u8 tid_num; ++ u8 vdev_id; ++ u16 sw_peer_id; ++ u32 info; ++ u32 ctrl; ++ u32 buffer_paddr_31_0; ++ u32 buffer_paddr_39_32; ++ u32 host_opaque_cookie; ++} __packed; ++ + struct htt_ppdu_stats_usr_cmn_array { + struct htt_tlv tlv_hdr; + u32 num_ppdu_stats; +@@ -1377,14 +1450,16 @@ struct htt_ppdu_stats_usr_cmn_array { + + struct htt_ppdu_user_stats { + u16 peer_id; ++ u16 delay_ba; + u32 tlv_flags; + bool is_valid_peer_id; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; ++ struct htt_ppdu_stats_user_common common; + }; + +-#define HTT_PPDU_STATS_MAX_USERS 8 ++#define HTT_PPDU_STATS_MAX_USERS 37 + #define HTT_PPDU_DESC_MAX_DEPTH 16 + + struct htt_ppdu_stats { +@@ -1393,7 +1468,7 @@ struct htt_ppdu_stats { + }; + + struct htt_ppdu_stats_info { +- u32 ppdu_id; ++ u32 tlv_bitmap, ppdu_id, frame_type, frame_ctrl, delay_ba, bar_num_users; + struct htt_ppdu_stats ppdu_stats; + struct list_head list; + }; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1252,9 +1252,10 @@ static int ath11k_htt_tlv_ppdu_stats_par + void *data) + { + struct htt_ppdu_stats_info *ppdu_info; +- struct htt_ppdu_user_stats *user_stats; ++ struct htt_ppdu_user_stats *user_stats = NULL; + int cur_user; + u16 peer_id; ++ u32 frame_type; + + ppdu_info = data; + +@@ -1267,6 +1268,26 @@ static int ath11k_htt_tlv_ppdu_stats_par + } + memcpy((void *)&ppdu_info->ppdu_stats.common, ptr, + sizeof(struct htt_ppdu_stats_common)); ++ frame_type = ++ FIELD_GET(HTT_PPDU_STATS_CMN_FLAGS_FRAME_TYPE_M, ++ ppdu_info->ppdu_stats.common.flags); ++ switch (frame_type) { ++ case HTT_STATS_FTYPE_TIDQ_DATA_SU: ++ case HTT_STATS_FTYPE_TIDQ_DATA_MU: ++ if (HTT_STATS_GET_FRAME_CTRL_TYPE(ppdu_info->frame_ctrl) <= HTT_STATS_FRAME_CTRL_TYPE_CTRL) ++ ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_CTRL; ++ else ++ ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_DATA; ++ break; ++ case HTT_STATS_FTYPE_SGEN_MU_BAR: ++ case HTT_STATS_FTYPE_SGEN_BAR: ++ ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_BAR; ++ break; ++ default: ++ ppdu_info->frame_type = HTT_STATS_PPDU_FTYPE_CTRL; ++ break; ++ } ++ + break; + case HTT_PPDU_STATS_TAG_USR_RATE: + if (len < sizeof(struct htt_ppdu_stats_user_rate)) { +@@ -1299,6 +1320,7 @@ static int ath11k_htt_tlv_ppdu_stats_par + peer_id); + if (cur_user < 0) + return -EINVAL; ++ ppdu_info->bar_num_users += 1; + user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; + user_stats->peer_id = peer_id; + user_stats->is_valid_peer_id = true; +@@ -1327,44 +1349,30 @@ static int ath11k_htt_tlv_ppdu_stats_par + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); + user_stats->tlv_flags |= BIT(tag); + break; +- } +- return 0; +-} +- +-int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, +- int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, +- const void *ptr, void *data), +- void *data) +-{ +- const struct htt_tlv *tlv; +- const void *begin = ptr; +- u16 tlv_tag, tlv_len; +- int ret = -EINVAL; +- +- while (len > 0) { +- if (len < sizeof(*tlv)) { +- ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", +- ptr - begin, len, sizeof(*tlv)); ++ case HTT_PPDU_STATS_TAG_USR_COMMON: ++ if (len < sizeof(struct htt_ppdu_stats_user_common)) { ++ ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", ++ len, tag); + return -EINVAL; + } +- tlv = (struct htt_tlv *)ptr; +- tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header); +- tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header); +- ptr += sizeof(*tlv); +- len -= sizeof(*tlv); +- +- if (tlv_len > len) { +- ath11k_err(ab, "htt tlv parse failure of tag %u at byte %zd (%zu bytes left, %u expected)\n", +- tlv_tag, ptr - begin, len, tlv_len); ++ peer_id = ((struct htt_ppdu_stats_user_common *)ptr)->sw_peer_id; ++ cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, ++ peer_id); ++ if (cur_user < 0) + return -EINVAL; +- } +- ret = iter(ab, tlv_tag, tlv_len, ptr, data); +- if (ret == -ENOMEM) +- return ret; +- +- ptr += tlv_len; +- len -= tlv_len; ++ user_stats = &ppdu_info->ppdu_stats.user_stats[cur_user]; ++ memcpy(&user_stats->common, ptr, ++ sizeof(struct htt_ppdu_stats_user_common)); ++ ppdu_info->frame_ctrl = FIELD_GET(HTT_PPDU_STATS_USR_CMN_CTL_FRM_CTRL, ++ user_stats->common.ctrl); ++ user_stats->delay_ba = FIELD_GET(HTT_PPDU_STATS_USR_CMN_FLAG_DELAYBA, ++ user_stats->common.info); ++ ppdu_info->delay_ba = user_stats->delay_ba; ++ break; ++ default: ++ break; + } ++ ppdu_info->tlv_bitmap |= BIT(tag); + return 0; + } + +@@ -1382,8 +1390,8 @@ ath11k_update_per_peer_tx_stats(struct a + struct htt_ppdu_stats_common *common = &ppdu_stats->common; + int ret; + u8 flags, mcs, nss, bw, sgi, dcm, rate_idx = 0; +- u32 succ_bytes = 0; +- u16 rate = 0, succ_pkts = 0; ++ u32 succ_bytes = 0, ppdu_type, mu_grpid, mu_pos; ++ u16 rate = 0, succ_pkts = 0, ru_tone, ru_start; + u32 tx_duration = 0; + u8 tid = HTT_PPDU_STATS_NON_QOS_TID; + bool is_ampdu = false; +@@ -1414,6 +1422,11 @@ ath11k_update_per_peer_tx_stats(struct a + mcs = HTT_USR_RATE_MCS(user_rate->rate_flags); + sgi = HTT_USR_RATE_GI(user_rate->rate_flags); + dcm = HTT_USR_RATE_DCM(user_rate->rate_flags); ++ ppdu_type = HTT_USR_RATE_PPDU_TYPE(user_rate->info1); ++ mu_grpid = HTT_USR_RATE_MU_GRPID(user_rate->info0); ++ mu_pos = HTT_USR_RATE_USR_POS(user_rate->info0); ++ ru_start = user_rate->ru_start; ++ ru_tone = user_rate->ru_end; + + /* Note: If host configured fixed rates and in some other special + * cases, the broadcast/management frames are sent in different rates. +@@ -1508,6 +1521,12 @@ ath11k_update_per_peer_tx_stats(struct a + peer_stats->ba_fails = + HTT_USR_CMPLTN_LONG_RETRY(usr_stats->cmpltn_cmn.flags) + + HTT_USR_CMPLTN_SHORT_RETRY(usr_stats->cmpltn_cmn.flags); ++ peer_stats->ppdu_type = ppdu_type; ++ peer_stats->ru_tones = ru_tone; ++ peer_stats->ru_start = ru_start; ++ peer_stats->mu_grpid = mu_grpid; ++ peer_stats->mu_pos = mu_pos; ++ peer_stats->ru_tones = arsta->txrate.he_ru_alloc; + + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) + ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); +@@ -1560,13 +1579,89 @@ struct htt_ppdu_stats_info *ath11k_dp_ht + return ppdu_info; + } + ++void ath11k_copy_to_delay_stats(struct ath11k_peer *peer, ++ struct htt_ppdu_user_stats* usr_stats) ++{ ++ peer->ppdu_stats_delayba.reserved0 = usr_stats->rate.reserved0; ++ peer->ppdu_stats_delayba.sw_peer_id = usr_stats->rate.sw_peer_id; ++ peer->ppdu_stats_delayba.info0 = usr_stats->rate.info0; ++ peer->ppdu_stats_delayba.ru_end = usr_stats->rate.ru_end; ++ peer->ppdu_stats_delayba.ru_start = usr_stats->rate.ru_start; ++ peer->ppdu_stats_delayba.info1 = usr_stats->rate.info1; ++ peer->ppdu_stats_delayba.rate_flags = usr_stats->rate.rate_flags; ++ peer->ppdu_stats_delayba.resp_rate_flags = usr_stats->rate.resp_rate_flags; ++ ++ peer->delayba_flag = true; ++} ++ ++void ath11k_copy_to_bar(struct ath11k_peer *peer, ++ struct htt_ppdu_user_stats* usr_stats) ++{ ++ usr_stats->rate.reserved0 = peer->ppdu_stats_delayba.reserved0; ++ usr_stats->rate.sw_peer_id = peer->ppdu_stats_delayba.sw_peer_id; ++ usr_stats->rate.info0 = peer->ppdu_stats_delayba.info0; ++ usr_stats->rate.ru_end = peer->ppdu_stats_delayba.ru_end; ++ usr_stats->rate.ru_start = peer->ppdu_stats_delayba.ru_start; ++ usr_stats->rate.info1 = peer->ppdu_stats_delayba.info1; ++ usr_stats->rate.rate_flags = peer->ppdu_stats_delayba.rate_flags; ++ usr_stats->rate.resp_rate_flags = peer->ppdu_stats_delayba.resp_rate_flags; ++ ++ peer->delayba_flag = false; ++} ++ ++int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, ++ int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, ++ const void *ptr, void *data), ++ void *data) ++{ ++ const struct htt_tlv *tlv; ++ const void *begin = ptr; ++ u16 tlv_tag, tlv_len; ++ int ret = -EINVAL; ++ struct htt_ppdu_stats_info * ppdu_info = NULL; ++ ++ if (data) { ++ ppdu_info = (struct htt_ppdu_stats_info *)data; ++ ppdu_info->tlv_bitmap = 0; ++ } ++ while (len > 0) { ++ if (len < sizeof(*tlv)) { ++ ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", ++ ptr - begin, len, sizeof(*tlv)); ++ return -EINVAL; ++ } ++ tlv = (struct htt_tlv *)ptr; ++ tlv_tag = FIELD_GET(HTT_TLV_TAG, tlv->header); ++ tlv_len = FIELD_GET(HTT_TLV_LEN, tlv->header); ++ ptr += sizeof(*tlv); ++ len -= sizeof(*tlv); ++ ++ if (tlv_len > len) { ++ ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", ++ tlv_tag, ptr - begin, len, tlv_len); ++ return -EINVAL; ++ } ++ ++ ret = iter(ab, tlv_tag, tlv_len, ptr, ppdu_info); ++ if (ret == -ENOMEM) ++ return ret; ++ ++ ptr += tlv_len; ++ len -= tlv_len; ++ } ++ return 0; ++} ++ + static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab, + struct sk_buff *skb) + { + struct ath11k_htt_ppdu_stats_msg *msg; + struct htt_ppdu_stats_info *ppdu_info; ++ struct ath11k_peer *peer = NULL; ++ struct htt_ppdu_user_stats* usr_stats = NULL; ++ u32 peer_id = 0; + struct ath11k *ar; +- int ret; ++ int ret, i; + u8 pdev_id; + u32 ppdu_id, len; + +@@ -1601,6 +1696,47 @@ static int ath11k_htt_pull_ppdu_stats(st + goto out_unlock_data; + } + ++ /* back up data rate tlv for all peers */ ++ if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_DATA && ++ (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON)) && ++ ppdu_info->delay_ba) { ++ ++ for (i = 0; i < ppdu_info->ppdu_stats.common.num_users; i++) { ++ peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id; ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ continue; ++ } ++ ++ usr_stats = &ppdu_info->ppdu_stats.user_stats[i]; ++ if (usr_stats->delay_ba) ++ ath11k_copy_to_delay_stats(peer, usr_stats); ++ spin_unlock_bh(&ab->base_lock); ++ } ++ } ++ ++ /* restore all peers' data rate tlv to mu-bar tlv */ ++ if (ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_BAR && ++ (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMMON))) { ++ ++ for (i = 0; i < ppdu_info->bar_num_users; i++) { ++ peer_id = ppdu_info->ppdu_stats.user_stats[i].peer_id; ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ continue; ++ } ++ ++ usr_stats = &ppdu_info->ppdu_stats.user_stats[i]; ++ if (peer->delayba_flag) ++ ath11k_copy_to_bar(peer, usr_stats); ++ spin_unlock_bh(&ab->base_lock); ++ } ++ } ++ + out_unlock_data: + spin_unlock_bh(&ar->data_lock); + +--- a/drivers/net/wireless/ath/ath11k/rx_desc.h ++++ b/drivers/net/wireless/ath/ath11k/rx_desc.h +@@ -1494,6 +1494,11 @@ struct hal_rx_desc { + } u; + } __packed; + ++#define MAX_USER_POS 8 ++#define MAX_MU_GROUP_ID 64 ++#define MAX_MU_GROUP_SHOW 16 ++#define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW) ++ + #define HAL_RX_RU_ALLOC_TYPE_MAX 6 + #define RU_26 1 + #define RU_52 2 +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -7,6 +7,17 @@ + #ifndef ATH11K_PEER_H + #define ATH11K_PEER_H + ++struct ppdu_user_delayba { ++ u8 reserved0; ++ u16 sw_peer_id; ++ u32 info0; ++ u16 ru_end; ++ u16 ru_start; ++ u32 info1; ++ u32 rate_flags; ++ u32 resp_rate_flags; ++}; ++ + struct ath11k_peer { + struct list_head list; + struct ieee80211_sta *sta; +@@ -36,6 +47,8 @@ struct ath11k_peer { + u16 sec_type_grp; + bool is_authorized; + bool dp_setup_done; ++ struct ppdu_user_delayba ppdu_stats_delayba; ++ bool delayba_flag; + }; + + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); diff --git a/package/kernel/mac80211/patches/nss/ath11k/080-ath11k-ethernet-rx-decap-offload.patch b/package/kernel/mac80211/patches/nss/ath11k/080-ath11k-ethernet-rx-decap-offload.patch new file mode 100644 index 00000000000000..3334ef52f8ec78 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/080-ath11k-ethernet-rx-decap-offload.patch @@ -0,0 +1,15 @@ +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -27,10 +27,10 @@ module_param_named(crypto_mode, ath11k_c + MODULE_PARM_DESC(crypto_mode, "crypto mode: 0-hardware, 1-software"); + + /* frame mode values are mapped as per enum ath11k_hw_txrx_mode */ +-unsigned int ath11k_frame_mode = ATH11K_HW_TXRX_NATIVE_WIFI; ++unsigned int ath11k_frame_mode = ATH11K_HW_TXRX_ETHERNET; + module_param_named(frame_mode, ath11k_frame_mode, uint, 0644); + MODULE_PARM_DESC(frame_mode, +- "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)"); ++ "Datapath frame mode (0: raw, 1: native wifi, 2: ethernet(default))"); + + bool ath11k_ftm_mode; + module_param_named(ftm_mode, ath11k_ftm_mode, bool, 0444); diff --git a/package/kernel/mac80211/patches/nss/ath11k/084-ath11k-fix-ul-ofdma-counter-always-zero-in-peer-stat.patch b/package/kernel/mac80211/patches/nss/ath11k/084-ath11k-fix-ul-ofdma-counter-always-zero-in-peer-stat.patch new file mode 100644 index 00000000000000..b46a32c09210ea --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/084-ath11k-fix-ul-ofdma-counter-always-zero-in-peer-stat.patch @@ -0,0 +1,82 @@ +From 3827a38706dcf081992fccf30957b29e81a25e5c Mon Sep 17 00:00:00 2001 +From: Miles Hu +Date: Mon, 25 Nov 2019 10:24:41 -0800 +Subject: [PATCH] ath11k: fix ul-ofdma counter always zero in peer stats + +The problem is caused by RSSI_LEGACY tlv is not handled properly. +All ul mu receiption information need to be extracted from the tlv. + +Signed-off-by: Miles Hu +--- + drivers/net/wireless/ath/ath11k/debugfs_sta.c | 7 ------- + drivers/net/wireless/ath/ath11k/hal_rx.c | 17 +++++++++++++++++ + drivers/net/wireless/ath/ath11k/hal_rx.h | 8 ++++++++ + 3 files changed, 25 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -525,13 +525,6 @@ static ssize_t ath11k_dbg_sta_dump_rx_st + rx_stats->byte_stats.rx_rate[i], + (i + 1) % (he_rates_avail ? 12 : 8) ? "\t" : "\n"); + +- len += scnprintf(buf + len, size - len, +- "\nDCM: %llu\nRU: 26 %llu 52: %llu 106: %llu 242: %llu 484: %llu 996: %llu\n", +- rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], +- rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], +- rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], +- rx_stats->ru_alloc_cnt[5]); +- + len += scnprintf(buf + len, size - len, "\n"); + + spin_unlock_bh(&ar->ab->base_lock); +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -1478,6 +1478,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ab->wmi_ab.svc_map); + struct hal_rx_phyrx_rssi_legacy_info *rssi = + (struct hal_rx_phyrx_rssi_legacy_info *)tlv_data; ++ u32 reception_type = 0; + + /* TODO: Please note that the combined rssi will not be accurate + * in MU case. Rssi in MU needs to be retrieved from +@@ -1487,6 +1488,22 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB, + __le32_to_cpu(rssi->info0)); + ++ reception_type = ++ FIELD_GET(HAL_RX_PHYRX_RSSI_LEGACY_INFO_RSVD1_RECEPTION, ++ __le32_to_cpu(rssi->rsvd[0])); ++ ++ switch (reception_type) { ++ case HAL_RECEPTION_TYPE_ULOFMDA: ++ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; ++ break; ++ case HAL_RECEPTION_TYPE_ULMIMO: ++ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; ++ break; ++ default: ++ ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; ++ break; ++ } ++ + if (db2dbm) { + for (i = 0; i < ARRAY_SIZE(rssi->preamble); i++) { + ppdu_info->rssi_chain_pri20[i] = +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -414,6 +414,15 @@ struct hal_rx_he_sig_b2_ofdma_info { + + #define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB GENMASK(15, 8) + ++#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_RSVD1_RECEPTION GENMASK(3, 0) ++ ++enum hal_rx_ul_reception_type { ++ HAL_RECEPTION_TYPE_ULOFMDA, ++ HAL_RECEPTION_TYPE_ULMIMO, ++ HAL_RECEPTION_TYPE_OTHER, ++ HAL_RECEPTION_TYPE_FRAMELESS ++}; ++ + #define HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20 GENMASK(7, 0) + + struct hal_rx_phyrx_chain_rssi { diff --git a/package/kernel/mac80211/patches/nss/ath11k/087-ath11k-fix-ul-ofdma-counter-increamenting-improperly.patch b/package/kernel/mac80211/patches/nss/ath11k/087-ath11k-fix-ul-ofdma-counter-increamenting-improperly.patch new file mode 100644 index 00000000000000..52e2509b796c3d --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/087-ath11k-fix-ul-ofdma-counter-increamenting-improperly.patch @@ -0,0 +1,51 @@ + drivers/net/wireless/ath/ath11k/dp_rx.c | 3 ++- + drivers/net/wireless/ath/ath11k/hal_rx.c | 3 +++ + drivers/net/wireless/ath/ath11k/hal_rx.h | 2 ++ + 3 files changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -5451,8 +5451,11 @@ int ath11k_dp_rx_process_mon_status(stru + goto next_skb; + } + +- arsta = ath11k_sta_to_arsta(peer->sta); +- ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); ++ if ((ppdu_info->fc_valid) && ++ (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { ++ arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); ++ } + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -900,6 +900,9 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->ast_index = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX, + __le32_to_cpu(eu_stats->info2)); ++ ppdu_info->fc_valid = ++ FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID, ++ __le32_to_cpu(eu_stats->info1)); + ppdu_info->tid = + ffs(FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO7_TID_BITMAP, + __le32_to_cpu(eu_stats->info7))) - 1; +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -70,6 +70,7 @@ enum hal_rx_reception_type { + }; + + #define HAL_RX_FCS_LEN 4 ++#define HAL_AST_IDX_INVALID 0xFFFF + + enum hal_rx_mon_status { + HAL_RX_MON_STATUS_PPDU_NOT_DONE, +@@ -171,6 +172,7 @@ struct hal_rx_mon_ppdu_info { + u8 rssi_comb; + u8 rssi_chain_pri20[HAL_RX_MAX_NSS]; + u16 tid; ++ u8 fc_valid; + u16 ht_flags; + u16 vht_flags; + u16 he_flags; diff --git a/package/kernel/mac80211/patches/nss/ath11k/108-ath11k-enable-ul-ofdma-ru-allocation-in-peer-stats.patch b/package/kernel/mac80211/patches/nss/ath11k/108-ath11k-enable-ul-ofdma-ru-allocation-in-peer-stats.patch new file mode 100644 index 00000000000000..18477cd5234f1b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/108-ath11k-enable-ul-ofdma-ru-allocation-in-peer-stats.patch @@ -0,0 +1,452 @@ +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -524,6 +524,12 @@ static ssize_t ath11k_dbg_sta_dump_rx_st + len += scnprintf(buf + len, size - len, "%10llu%s", + rx_stats->byte_stats.rx_rate[i], + (i + 1) % (he_rates_avail ? 12 : 8) ? "\t" : "\n"); ++ len += scnprintf(buf + len, size - len, ++ "\nDCM: %llu\nRU26: %llu \nRU52: %llu \nRU106: %llu \nRU242: %llu \nRU484: %llu \nRU996: %llu\n", ++ rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], ++ rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], ++ rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], ++ rx_stats->ru_alloc_cnt[5]); + + len += scnprintf(buf + len, size - len, "\n"); + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2901,11 +2901,12 @@ exit: + static void + ath11k_dp_rx_update_peer_rate_table_stats(struct ath11k_rx_peer_stats *rx_stats, + struct hal_rx_mon_ppdu_info *ppdu_info, ++ struct hal_rx_user_status* user_stats, + u32 num_msdu) + { + u32 rate_idx = 0; +- u32 mcs_idx = ppdu_info->mcs; +- u32 nss_idx = ppdu_info->nss - 1; ++ u32 mcs_idx = (user_stats) ? user_stats->mcs : ppdu_info->mcs; ++ u32 nss_idx = (user_stats) ? user_stats->nss - 1 : ppdu_info->nss - 1; + u32 bw_idx = ppdu_info->bw; + u32 gi_idx = ppdu_info->gi; + +@@ -2927,10 +2928,13 @@ ath11k_dp_rx_update_peer_rate_table_stat + } + + rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu; +- rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; ++ if (user_stats) ++ rx_stats->byte_stats.rx_rate[rate_idx] += user_stats->mpdu_ok_byte_count; ++ else ++ rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; + } + +-static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, ++static void ath11k_dp_rx_update_peer_su_stats(struct ath11k_sta *arsta, + struct hal_rx_mon_ppdu_info *ppdu_info) + { + struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats; +@@ -2988,7 +2992,6 @@ static void ath11k_dp_rx_update_peer_sta + rx_stats->num_mpdu_fcs_ok += ppdu_info->num_mpdu_fcs_ok; + rx_stats->num_mpdu_fcs_err += ppdu_info->num_mpdu_fcs_err; + rx_stats->dcm_count += ppdu_info->dcm; +- rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; + + BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > + ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); +@@ -3006,10 +3009,10 @@ static void ath11k_dp_rx_update_peer_sta + + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N && + ppdu_info->mcs <= HAL_RX_MAX_MCS_HT) { +- rx_stats->pkt_stats.ht_mcs_count[ppdu_info->mcs] += num_msdu; +- rx_stats->byte_stats.ht_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; +- /* To fit into rate table for HT packets */ +- ppdu_info->mcs = ppdu_info->mcs % 8; ++ rx_stats->pkt_stats.ht_mcs_count[ppdu_info->mcs] += num_msdu; ++ rx_stats->byte_stats.ht_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; ++ /* To fit into rate table for HT packets */ ++ ppdu_info->mcs = ppdu_info->mcs % 8; + } + + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC && +@@ -3042,7 +3045,120 @@ static void ath11k_dp_rx_update_peer_sta + rx_stats->byte_stats.bw_count[ppdu_info->bw] += ppdu_info->mpdu_len; + } + +- ath11k_dp_rx_update_peer_rate_table_stats(rx_stats, ppdu_info, num_msdu); ++ ath11k_dp_rx_update_peer_rate_table_stats(rx_stats, ppdu_info, NULL, num_msdu); ++ ++} ++ ++static void ath11k_dp_rx_update_user_stats(struct ath11k *ar, ++ struct hal_rx_mon_ppdu_info *ppdu_info, ++ u32 uid) ++{ ++ struct ath11k_sta *arsta = NULL; ++ struct ath11k_rx_peer_stats *rx_stats = NULL; ++ struct hal_rx_user_status* user_stats = &ppdu_info->userstats[uid]; ++ struct ath11k_peer *peer; ++ u32 num_msdu; ++ ++ if (user_stats->ast_index == 0 || user_stats->ast_index == 0xFFFF) ++ return; ++ ++ peer = ath11k_peer_find_by_ast(ar->ab, user_stats->ast_index); ++ ++ if (peer == NULL) { ++ ath11k_warn(ar->ab, "peer ast idx %d can't be found\n", ++ user_stats->ast_index); ++ return; ++ } ++ ++ arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ rx_stats = arsta->rx_stats; ++ ++ if (!rx_stats) ++ return; ++ ++ arsta->rssi_comb = ppdu_info->rssi_comb; ++ ++ num_msdu = user_stats->tcp_msdu_count + user_stats->tcp_ack_msdu_count + ++ user_stats->udp_msdu_count + user_stats->other_msdu_count; ++ ++ rx_stats->num_msdu += num_msdu; ++ rx_stats->tcp_msdu_count += user_stats->tcp_msdu_count + ++ user_stats->tcp_ack_msdu_count; ++ rx_stats->udp_msdu_count += user_stats->udp_msdu_count; ++ rx_stats->other_msdu_count += user_stats->other_msdu_count; ++ ++ if (ppdu_info->ldpc < HAL_RX_SU_MU_CODING_MAX) ++ rx_stats->coding_count[ppdu_info->ldpc] += num_msdu; ++ ++ if (user_stats->tid <= IEEE80211_NUM_TIDS) ++ rx_stats->tid_count[user_stats->tid] += num_msdu; ++ ++ if (user_stats->preamble_type < HAL_RX_PREAMBLE_MAX) ++ rx_stats->pream_cnt[user_stats->preamble_type] += num_msdu; ++ ++ if (ppdu_info->reception_type < HAL_RX_RECEPTION_TYPE_MAX) ++ rx_stats->reception_type[ppdu_info->reception_type] += num_msdu; ++ ++ if (ppdu_info->is_stbc) ++ rx_stats->stbc_count += num_msdu; ++ ++ if (ppdu_info->beamformed) ++ rx_stats->beamformed_count += num_msdu; ++ ++ if (user_stats->mpdu_cnt_fcs_ok > 1) ++ rx_stats->ampdu_msdu_count += num_msdu; ++ else ++ rx_stats->non_ampdu_msdu_count += num_msdu; ++ ++ rx_stats->num_mpdu_fcs_ok += user_stats->mpdu_cnt_fcs_ok; ++ rx_stats->num_mpdu_fcs_err += user_stats->mpdu_cnt_fcs_err; ++ rx_stats->dcm_count += ppdu_info->dcm; ++ if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA || ++ ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO) ++ rx_stats->ru_alloc_cnt[user_stats->ul_ofdma_ru_size] += num_msdu; ++ ++ rx_stats->rx_duration += ppdu_info->rx_duration; ++ arsta->rx_duration = rx_stats->rx_duration; ++ ++ if (user_stats->nss > 0 && user_stats->nss <= HAL_RX_MAX_NSS) { ++ rx_stats->pkt_stats.nss_count[user_stats->nss - 1] += num_msdu; ++ rx_stats->byte_stats.nss_count[user_stats->nss - 1] += user_stats->mpdu_ok_byte_count; ++ } ++ ++ if (user_stats->preamble_type == HAL_RX_PREAMBLE_11AX && ++ user_stats->mcs <= HAL_RX_MAX_MCS_HE) { ++ rx_stats->pkt_stats.he_mcs_count[user_stats->mcs] += num_msdu; ++ rx_stats->byte_stats.he_mcs_count[user_stats->mcs] += user_stats->mpdu_ok_byte_count; ++ } ++ ++ if (ppdu_info->gi < HAL_RX_GI_MAX) { ++ rx_stats->pkt_stats.gi_count[ppdu_info->gi] += num_msdu; ++ rx_stats->byte_stats.gi_count[ppdu_info->gi] += user_stats->mpdu_ok_byte_count; ++ } ++ ++ if (ppdu_info->bw < HAL_RX_BW_MAX) { ++ rx_stats->pkt_stats.bw_count[ppdu_info->bw] += num_msdu; ++ rx_stats->byte_stats.bw_count[ppdu_info->bw] += user_stats->mpdu_ok_byte_count; ++ } ++ ++ ath11k_dp_rx_update_peer_rate_table_stats(rx_stats, ppdu_info, user_stats, num_msdu); ++} ++ ++static void ath11k_dp_rx_update_peer_mu_stats(struct ath11k *ar, ++ struct hal_rx_mon_ppdu_info *ppdu_info) ++{ ++ u32 num_users, i; ++ ++ if (!ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ return; ++ ++ num_users = ppdu_info->num_users; ++ if (num_users > HAL_MAX_UL_MU_USERS) ++ num_users = HAL_MAX_UL_MU_USERS; ++ ++ for (i = 0; i < num_users; i++) { ++ ath11k_dp_rx_update_user_stats(ar, ppdu_info, i); ++ } + + } + +@@ -5380,6 +5496,55 @@ static void ath11k_dp_rx_mon_dest_proces + } + } + ++void ath11k_dp_rx_mon_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info) ++{ ++ struct hal_rx_user_status *rx_user_status; ++ u32 num_users; ++ uint32_t i; ++ uint32_t mu_ul_user_v0_word0; ++ uint32_t mu_ul_user_v0_word1; ++ uint32_t ru_size; ++ ++ if (!(ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA || ++ ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO)) ++ return; ++ ++ num_users = ppdu_info->num_users; ++ if (num_users > HAL_MAX_UL_MU_USERS) ++ num_users = HAL_MAX_UL_MU_USERS; ++ ++ for (i = 0; i < num_users; i++) { ++ rx_user_status = &ppdu_info->userstats[i]; ++ mu_ul_user_v0_word0 = ++ rx_user_status->ul_ofdma_user_v0_word0; ++ mu_ul_user_v0_word1 = ++ rx_user_status->ul_ofdma_user_v0_word1; ++ ++ if (FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VALID, ++ mu_ul_user_v0_word0) && ++ !FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VER, ++ mu_ul_user_v0_word0)) { ++ rx_user_status->mcs = ++ FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W1_MCS, ++ mu_ul_user_v0_word1); ++ rx_user_status->nss = ++ FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W1_NSS, ++ mu_ul_user_v0_word1) + 1; ++ ++ rx_user_status->ofdma_info_valid = 1; ++ rx_user_status->ul_ofdma_ru_start_index = ++ FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_START, ++ mu_ul_user_v0_word1); ++ ++ ru_size = FIELD_GET(HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_SIZE, ++ mu_ul_user_v0_word1); ++ rx_user_status->ul_ofdma_ru_width = ru_size; ++ rx_user_status->ul_ofdma_ru_size = ru_size; ++ } ++ } ++ ++} ++ + int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) + { +@@ -5453,8 +5618,13 @@ int ath11k_dp_rx_process_mon_status(stru + + if ((ppdu_info->fc_valid) && + (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { +- arsta = (struct ath11k_sta *)peer->sta->drv_priv; +- ath11k_dp_rx_update_peer_stats(arsta, ppdu_info); ++ if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { ++ arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ ath11k_dp_rx_update_peer_su_stats(arsta, ppdu_info); ++ } else { ++ ath11k_dp_rx_mon_process_ulofdma(ppdu_info); ++ ath11k_dp_rx_update_peer_mu_stats(ar, ppdu_info); ++ } + } + + if (ath11k_debugfs_is_pktlog_peer_valid(ar, peer->addr)) +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -804,7 +804,6 @@ void ath11k_hal_reo_init_cmd_ring(struct + } + } + +-#define HAL_MAX_UL_MU_USERS 37 + static inline void + ath11k_hal_rx_handle_ofdma_info(void *rx_tlv, + struct hal_rx_user_status *rx_user_status) +@@ -836,6 +835,8 @@ ath11k_hal_rx_populate_mu_user_info(void + { + rx_user_status->ast_index = ppdu_info->ast_index; + rx_user_status->tid = ppdu_info->tid; ++ rx_user_status->tcp_ack_msdu_count = ++ ppdu_info->tcp_ack_msdu_count; + rx_user_status->tcp_msdu_count = + ppdu_info->tcp_msdu_count; + rx_user_status->udp_msdu_count = +@@ -859,6 +860,9 @@ ath11k_hal_rx_populate_mu_user_info(void + ppdu_info->num_mpdu_fcs_ok; + rx_user_status->mpdu_cnt_fcs_err = + ppdu_info->num_mpdu_fcs_err; ++ memcpy(&rx_user_status->mpdu_fcs_ok_bitmap[0], &ppdu_info->mpdu_fcs_ok_bitmap[0], ++ HAL_RX_NUM_WORDS_PER_PPDU_BITMAP * ++ sizeof(ppdu_info->mpdu_fcs_ok_bitmap[0])); + + ath11k_hal_rx_populate_byte_count(rx_tlv, ppdu_info, rx_user_status); + } +@@ -888,6 +892,14 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + __le32_to_cpu(ppdu_start->info0)); + ppdu_info->chan_num = __le32_to_cpu(ppdu_start->chan_num); + ppdu_info->ppdu_ts = __le32_to_cpu(ppdu_start->ppdu_start_ts); ++ ++ if (ppdu_info->ppdu_id != ppdu_info->last_ppdu_id) { ++ ppdu_info->last_ppdu_id = ppdu_info->ppdu_id; ++ ppdu_info->num_users = 0; ++ memset(&ppdu_info->mpdu_fcs_ok_bitmap, 0, ++ HAL_RX_NUM_WORDS_PER_PPDU_BITMAP * ++ sizeof(ppdu_info->mpdu_fcs_ok_bitmap[0])); ++ } + break; + } + case HAL_RX_PPDU_END_USER_STATS: { +@@ -942,15 +954,16 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + + if (userid < HAL_MAX_UL_MU_USERS) { + struct hal_rx_user_status *rxuser_stats = +- &ppdu_info->userstats; ++ &ppdu_info->userstats[userid]; ++ ppdu_info->num_users += 1; + + ath11k_hal_rx_handle_ofdma_info(tlv_data, rxuser_stats); + ath11k_hal_rx_populate_mu_user_info(tlv_data, ppdu_info, + rxuser_stats); + } +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[0] = ++ ppdu_info->mpdu_fcs_ok_bitmap[0] = + __le32_to_cpu(eu_stats->rsvd1[0]); +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[1] = ++ ppdu_info->mpdu_fcs_ok_bitmap[1] = + __le32_to_cpu(eu_stats->rsvd1[1]); + + break; +@@ -958,12 +971,12 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + case HAL_RX_PPDU_END_USER_STATS_EXT: { + struct hal_rx_ppdu_end_user_stats_ext *eu_stats = + (struct hal_rx_ppdu_end_user_stats_ext *)tlv_data; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[2] = eu_stats->info1; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[3] = eu_stats->info2; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[4] = eu_stats->info3; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[5] = eu_stats->info4; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[6] = eu_stats->info5; +- ppdu_info->userstats.mpdu_fcs_ok_bitmap[7] = eu_stats->info6; ++ ppdu_info->mpdu_fcs_ok_bitmap[2] = eu_stats->info1; ++ ppdu_info->mpdu_fcs_ok_bitmap[3] = eu_stats->info2; ++ ppdu_info->mpdu_fcs_ok_bitmap[4] = eu_stats->info3; ++ ppdu_info->mpdu_fcs_ok_bitmap[5] = eu_stats->info4; ++ ppdu_info->mpdu_fcs_ok_bitmap[6] = eu_stats->info5; ++ ppdu_info->mpdu_fcs_ok_bitmap[7] = eu_stats->info6; + break; + } + case HAL_PHYRX_HT_SIG: { +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -72,6 +72,10 @@ enum hal_rx_reception_type { + #define HAL_RX_FCS_LEN 4 + #define HAL_AST_IDX_INVALID 0xFFFF + ++#define HAL_MAX_UL_MU_USERS 37 ++#define HAL_RX_MAX_MPDU 256 ++#define HAL_RX_NUM_WORDS_PER_PPDU_BITMAP (HAL_RX_MAX_MPDU >> 5) ++ + enum hal_rx_mon_status { + HAL_RX_MON_STATUS_PPDU_NOT_DONE, + HAL_RX_MON_STATUS_PPDU_DONE, +@@ -82,14 +86,15 @@ struct hal_rx_user_status { + u32 mcs:4, + nss:3, + ofdma_info_valid:1, +- dl_ofdma_ru_start_index:7, +- dl_ofdma_ru_width:7, +- dl_ofdma_ru_size:8; ++ ul_ofdma_ru_start_index:7, ++ ul_ofdma_ru_width:7, ++ ul_ofdma_ru_size:8; + u32 ul_ofdma_user_v0_word0; + u32 ul_ofdma_user_v0_word1; + u32 ast_index; + u32 tid; + u16 tcp_msdu_count; ++ u16 tcp_ack_msdu_count; + u16 udp_msdu_count; + u16 other_msdu_count; + u16 frame_control; +@@ -103,7 +108,7 @@ struct hal_rx_user_status { + u8 rs_flags; + u32 mpdu_cnt_fcs_ok; + u32 mpdu_cnt_fcs_err; +- u32 mpdu_fcs_ok_bitmap[8]; ++ u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; + u32 mpdu_ok_byte_count; + u32 mpdu_err_byte_count; + }; +@@ -144,6 +149,7 @@ struct hal_sw_mon_ring_entries { + + struct hal_rx_mon_ppdu_info { + u32 ppdu_id; ++ u32 last_ppdu_id; + u32 ppdu_ts; + u32 num_mpdu_fcs_ok; + u32 num_mpdu_fcs_err; +@@ -212,9 +218,20 @@ struct hal_rx_mon_ppdu_info { + u8 ltf_size; + u8 rxpcu_filter_pass; + char rssi_chain[8][8]; +- struct hal_rx_user_status userstats; ++ u32 num_users; ++ u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; ++ struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; + }; + ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VALID BIT(30) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VER BIT(31) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_NSS GENMASK(2, 0) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_MCS GENMASK(6, 3) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_LDPC BIT(7) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_DCM BIT(8) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_START GENMASK(15, 9) ++#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_SIZE GENMASK(18, 16) ++ + #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) + + struct hal_rx_ppdu_start { +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -93,6 +93,20 @@ struct ath11k_peer *ath11k_peer_find_by_ + return NULL; + } + ++struct ath11k_peer *ath11k_peer_find_by_ast(struct ath11k_base *ab, ++ int ast_hash) ++{ ++ struct ath11k_peer *peer; ++ ++ lockdep_assert_held(&ab->base_lock); ++ ++ list_for_each_entry(peer, &ab->peers, list) ++ if (ast_hash == peer->ast_hash) ++ return peer; ++ ++ return NULL; ++} ++ + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) + { + struct ath11k_peer *peer; +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -59,6 +59,7 @@ struct ath11k_peer *ath11k_peer_find(str + struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, + const u8 *addr); + struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); ++struct ath11k_peer *ath11k_peer_find_by_ast(struct ath11k_base *ab, int ast_hash); + void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id); + int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr); + int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, diff --git a/package/kernel/mac80211/patches/nss/ath11k/113-ath11k-add-8023-undecap-support.patch b/package/kernel/mac80211/patches/nss/ath11k/113-ath11k-add-8023-undecap-support.patch new file mode 100644 index 00000000000000..2ba2411c3ef54b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/113-ath11k-add-8023-undecap-support.patch @@ -0,0 +1,55 @@ +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2305,6 +2305,42 @@ static void ath11k_dp_rx_h_undecap_eth(s + ether_addr_copy(ieee80211_get_SA(hdr), sa); + } + ++static void ath11k_dp_rx_h_undecap_snap(struct ath11k *ar, ++ struct sk_buff *msdu, ++ u8 *first_hdr, ++ enum hal_encrypt_type enctype, ++ struct ieee80211_rx_status *status) ++{ ++ struct ieee80211_hdr *hdr; ++ size_t hdr_len; ++ u8 l3_pad_bytes; ++ struct hal_rx_desc *rx_desc; ++ ++ /* Delivered decapped frame: ++ * [amsdu header] <-- replaced with 802.11 hdr ++ * [rfc1042/llc] ++ * [payload] ++ */ ++ ++ rx_desc = (void *)msdu->data - sizeof(*rx_desc); ++ l3_pad_bytes = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, rx_desc); ++ ++ skb_put(msdu, l3_pad_bytes); ++ skb_pull(msdu, sizeof(struct ath11k_dp_amsdu_subframe_hdr) + l3_pad_bytes); ++ ++ hdr = (struct ieee80211_hdr *)first_hdr; ++ hdr_len = ieee80211_hdrlen(hdr->frame_control); ++ ++ if (!(status->flag & RX_FLAG_IV_STRIPPED)) { ++ memcpy(skb_push(msdu, ++ ath11k_dp_rx_crypto_param_len(ar, enctype)), ++ (void *)hdr + hdr_len, ++ ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ } ++ ++ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); ++} ++ + static void ath11k_dp_rx_h_undecap(struct ath11k *ar, struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, + enum hal_encrypt_type enctype, +@@ -2346,7 +2382,8 @@ static void ath11k_dp_rx_h_undecap(struc + enctype, status); + break; + case DP_RX_DECAP_TYPE_8023: +- /* TODO: Handle undecap for these formats */ ++ ath11k_dp_rx_h_undecap_snap(ar, msdu, first_hdr, ++ enctype, status); + break; + } + } diff --git a/package/kernel/mac80211/patches/nss/ath11k/142-ath11k-adding-support-for-mgmt-frame-stats.patch b/package/kernel/mac80211/patches/nss/ath11k/142-ath11k-adding-support-for-mgmt-frame-stats.patch new file mode 100644 index 00000000000000..c069cc8ff9f58a --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/142-ath11k-adding-support-for-mgmt-frame-stats.patch @@ -0,0 +1,292 @@ +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -314,6 +314,16 @@ struct ath11k_rekey_data { + bool enable_offload; + }; + ++#define ATH11K_STATS_MGMT_FRM_TYPE_MAX 16 ++ ++struct ath11k_mgmt_frame_stats { ++ u32 tx_succ_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++ u32 tx_fail_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++ u32 rx_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++ u32 tx_compl_succ[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++ u32 tx_compl_fail[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; ++}; ++ + struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; +@@ -372,6 +382,8 @@ struct ath11k_vif { + #ifdef CPTCFG_ATH11K_DEBUGFS + struct dentry *debugfs_twt; + #endif /* CPTCFG_ATH11K_DEBUGFS */ ++ ++ struct ath11k_mgmt_frame_stats mgmt_stats; + }; + + struct ath11k_vif_iter { +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -1588,6 +1588,87 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++static ssize_t ath11k_dump_mgmt_stats(struct file *file, char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k *ar = file->private_data; ++ struct ath11k_vif *arvif = NULL; ++ struct ath11k_mgmt_frame_stats *mgmt_stats; ++ int len = 0, ret, i; ++ int size = (TARGET_NUM_VDEVS(ar->ab) - 1) * 1500; ++ char *buf; ++ const char *mgmt_frm_type[ATH11K_STATS_MGMT_FRM_TYPE_MAX-1] = {"assoc_req", "assoc_resp", ++ "reassoc_req", "reassoc_resp", ++ "probe_req", "probe_resp", ++ "timing_advertisement", "reserved", ++ "beacon", "atim", "disassoc", ++ "auth", "deauth", "action", "action_no_ack"}; ++ ++ if (ar->state != ATH11K_STATE_ON) ++ return -ENETDOWN; ++ ++ buf = kzalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mutex_lock(&ar->conf_mutex); ++ spin_lock_bh(&ar->data_lock); ++ ++ list_for_each_entry (arvif, &ar->arvifs, list) { ++ if (!arvif) ++ break; ++ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ continue; ++ ++ mgmt_stats = &arvif->mgmt_stats; ++ len += scnprintf(buf + len, size - len, "MGMT frame stats for vdev %u :\n", ++ arvif->vdev_id); ++ len += scnprintf(buf + len, size - len, " TX stats :\n "); ++ len += scnprintf(buf + len, size - len, " Success frames:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) ++ len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], ++ mgmt_stats->tx_succ_cnt[i]); ++ ++ len += scnprintf(buf + len, size - len, " Failed frames:\n"); ++ ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) ++ len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], ++ mgmt_stats->tx_fail_cnt[i]); ++ ++ len += scnprintf(buf + len, size - len, " RX stats :\n"); ++ len += scnprintf(buf + len, size - len, " Success frames:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) ++ len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], ++ mgmt_stats->rx_cnt[i]); ++ ++ len += scnprintf(buf + len, size - len, " Tx completion stats :\n"); ++ len += scnprintf(buf + len, size - len, " success completions:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) ++ len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], ++ mgmt_stats->tx_compl_succ[i]); ++ len += scnprintf(buf + len, size - len, " failure completions:\n"); ++ for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) ++ len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], ++ mgmt_stats->tx_compl_fail[i]); ++ } ++ ++ spin_unlock_bh(&ar->data_lock); ++ ++ if (len > size) ++ len = size; ++ ++ ret = simple_read_from_buffer(ubuf, count, ppos, buf, len); ++ mutex_unlock(&ar->conf_mutex); ++ kfree(buf); ++ return ret; ++} ++ ++static const struct file_operations fops_dump_mgmt_stats = { ++ .read = ath11k_dump_mgmt_stats, ++ .open = simple_open ++}; ++ + int ath11k_debugfs_register(struct ath11k *ar) + { + struct ath11k_base *ab = ar->ab; +@@ -1620,6 +1701,9 @@ int ath11k_debugfs_register(struct ath11 + debugfs_create_file("fw_dbglog_config", 0600, + ar->debug.debugfs_pdev, ar, + &fops_fw_dbglog); ++ debugfs_create_file("dump_mgmt_stats", 0644, ++ ar->debug.debugfs_pdev, ar, ++ &fops_dump_mgmt_stats); + + if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { + debugfs_create_file("dfs_simulate_radar", 0200, +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6148,9 +6148,9 @@ static int ath11k_mac_mgmt_tx(struct ath + */ + if (is_prb_rsp && + atomic_read(&ar->num_pending_mgmt_tx) > ATH11K_PRB_RSP_DROP_THRESHOLD) { +- ath11k_warn(ar->ab, ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "dropping probe response as pending queue is almost full\n"); +- return -ENOSPC; ++ return -EBUSY; + } + + if (skb_queue_len_lockless(q) >= ATH11K_TX_MGMT_NUM_PENDING_MAX) { +@@ -6176,9 +6176,11 @@ static void ath11k_mac_op_tx(struct ieee + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_key_conf *key = info->control.hw_key; ++ struct ath11k_mgmt_frame_stats *mgmt_stats = &arvif->mgmt_stats; + struct ath11k_sta *arsta = NULL; + u32 info_flags = info->flags; + bool is_prb_rsp; ++ u16 frm_type = 0; + int ret; + + memset(skb_cb, 0, sizeof(*skb_cb)); +@@ -6192,12 +6194,21 @@ static void ath11k_mac_op_tx(struct ieee + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { + skb_cb->flags |= ATH11K_SKB_HW_80211_ENCAP; + } else if (ieee80211_is_mgmt(hdr->frame_control)) { ++ frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); + is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); + ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp); + if (ret) { +- ath11k_warn(ar->ab, "failed to queue management frame %d\n", +- ret); ++ if (ret != -EBUSY) ++ ath11k_warn(ar->ab, "failed to queue management frame %d\n", ++ ret); + ieee80211_free_txskb(ar->hw, skb); ++ spin_lock_bh(&ar->data_lock); ++ mgmt_stats->tx_fail_cnt[frm_type]++; ++ spin_unlock_bh(&ar->data_lock); ++ } else { ++ spin_lock_bh(&ar->data_lock); ++ mgmt_stats->tx_succ_cnt[frm_type]++; ++ spin_unlock_bh(&ar->data_lock); + } + return; + } +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -458,6 +458,7 @@ int ath11k_peer_create(struct ath11k *ar + + peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; + peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; ++ peer->vif = arvif->vif; + + if (sta) { + arsta = ath11k_sta_to_arsta(sta); +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -21,6 +21,7 @@ struct ppdu_user_delayba { + struct ath11k_peer { + struct list_head list; + struct ieee80211_sta *sta; ++ struct ieee80211_vif *vif; + int vdev_id; + u8 addr[ETH_ALEN]; + int peer_id; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5823,6 +5823,12 @@ static int wmi_process_mgmt_tx_comp(stru + struct sk_buff *msdu; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; ++ struct ieee80211_hdr *hdr; ++ struct ath11k_peer *peer; ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ struct ath11k_mgmt_frame_stats *mgmt_stats; ++ u16 frm_type; + int num_mgmt; + + spin_lock_bh(&ar->txmgmt_idr_lock); +@@ -5850,6 +5856,31 @@ static int wmi_process_mgmt_tx_comp(stru + info->status.ack_signal = tx_compl_param->ack_rssi; + } + ++ hdr = (struct ieee80211_hdr *)msdu->data; ++ frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); ++ ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2); ++ if (!peer) { ++ spin_unlock_bh(&ar->ab->base_lock); ++ ath11k_warn(ar->ab, "failed to find peer to update txcompl mgmt stats\n"); ++ goto skip_mgmt_stats; ++ } ++ ++ vif = peer->vif; ++ spin_unlock_bh(&ar->ab->base_lock); ++ ++ spin_lock_bh(&ar->data_lock); ++ arvif = ath11k_vif_to_arvif(vif); ++ mgmt_stats = &arvif->mgmt_stats; ++ ++ if (!tx_compl_param->status) ++ mgmt_stats->tx_compl_succ[frm_type]++; ++ else ++ mgmt_stats->tx_compl_fail[frm_type]++; ++ spin_unlock_bh(&ar->data_lock); ++ ++skip_mgmt_stats: + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); +@@ -7519,6 +7550,11 @@ static void ath11k_mgmt_rx_event(struct + struct ieee80211_hdr *hdr; + u16 fc; + struct ieee80211_supported_band *sband; ++ struct ath11k_peer *peer; ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ struct ath11k_mgmt_frame_stats *mgmt_stats; ++ u16 frm_type = 0; + + if (ath11k_pull_mgmt_rx_params_tlv(ab, skb, &rx_ev) != 0) { + ath11k_warn(ab, "failed to extract mgmt rx event"); +@@ -7584,7 +7620,34 @@ static void ath11k_mgmt_rx_event(struct + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); ++ frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, fc); ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_addr(ab, hdr->addr1); ++ if(!peer) ++ peer = ath11k_peer_find_by_addr(ab, hdr->addr3); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ goto skip_mgmt_stats; ++ } ++ ++ vif = peer->vif; ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ if (!vif) ++ goto skip_mgmt_stats; ++ ++ spin_lock_bh(&ar->data_lock); ++ ++ arvif = ath11k_vif_to_arvif(vif); ++ mgmt_stats = &arvif->mgmt_stats; ++ mgmt_stats->rx_cnt[frm_type]++; ++ ++ spin_unlock_bh(&ar->data_lock); + ++skip_mgmt_stats: + /* Firmware is guaranteed to report all essential management frames via + * WMI while it can deliver some extra via HTT. Since there can be + * duplicates split the reporting wrt monitor/sniffing. diff --git a/package/kernel/mac80211/patches/nss/ath11k/181-ath11k-remove-error-on-soc-debugfs-fail.patch b/package/kernel/mac80211/patches/nss/ath11k/181-ath11k-remove-error-on-soc-debugfs-fail.patch new file mode 100644 index 00000000000000..ca21c0b14297b2 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/181-ath11k-remove-error-on-soc-debugfs-fail.patch @@ -0,0 +1,146 @@ +From 41363c3109235a96d90d5946bbc01d1cc8dad47e Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Sun, 6 Sep 2020 11:01:38 +0530 +Subject: [PATCH] ath11k: update debugfs support for mupltiple radios in PCI +bus + +debugfs_ath11k struct is moved to ath11k_core, since its common +for both pci and ahb. + +Current ath11k_pci insmod fails if there are multiple PCI rdaios, +debugfs directory is created with soc_name and bus_id to allow +creating debugfs directory for second PCI radio. + +with this Debugfs entries looks like, + # ls -l /sys/kernel/debug/ath11k/ + ipq8074 hw2.0 qcn9000 hw1.0_0000:01:00.0 qcn9000 hw1.0_0001:01:00.0 + + # ls -l /sys/kernel/debug/ath11k/ipq8074 hw2.0/ + mac0 mac1 simulate_fw_crash soc_dp_stats + + # ls -l /sys/kernel/debug/ath11k/qcn9000 hw1.0_0000:01:00.0 + mac0 simulate_fw_crash soc_dp_stats + + # /sys/kernel/debug/ath11k/qcn9000 hw1.0_0001:01:00.0: + mac0 simulate_fw_crash soc_dp_stats + +Signed-off-by: Anilkumar Kolli +--- + drivers/net/wireless/ath/ath11k/core.c | 12 +++++++ + drivers/net/wireless/ath/ath11k/core.h | 1 - + drivers/net/wireless/ath/ath11k/debugfs.c | 57 ++++++++++++++++++++++++------ + drivers/net/wireless/ath/ath11k/debugfs.h | 11 ++++++ + 5 files changed, 72 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -2189,5 +2189,17 @@ err_sc_free: + } + EXPORT_SYMBOL(ath11k_core_alloc); + ++int ath11k_init(void) ++{ ++ return ath11k_debugfs_create(); ++} ++module_init(ath11k_init); ++ ++void ath11k_exit(void) ++{ ++ ath11k_debugfs_destroy(); ++} ++module_exit(ath11k_exit); ++ + MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11ax wireless LAN cards."); + MODULE_LICENSE("Dual BSD/GPL"); +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -16,6 +16,8 @@ + #include "peer.h" + #include "hif.h" + ++struct dentry *debugfs_ath11k; ++ + static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = { + "REO2SW1_RING", + "REO2SW2_RING", +@@ -991,8 +993,6 @@ int ath11k_debugfs_pdev_create(struct at + + void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab) + { +- debugfs_remove_recursive(ab->debugfs_soc); +- ab->debugfs_soc = NULL; + } + + int ath11k_debugfs_soc_create(struct ath11k_base *ab) +@@ -1045,6 +1045,24 @@ void ath11k_debugfs_soc_destroy(struct a + } + EXPORT_SYMBOL(ath11k_debugfs_soc_destroy); + ++int ath11k_debugfs_create(void) ++{ ++ debugfs_ath11k = debugfs_create_dir("ath11k", NULL); ++ if (IS_ERR_OR_NULL(debugfs_ath11k)) { ++ if (IS_ERR(debugfs_ath11k)) ++ return PTR_ERR(debugfs_ath11k); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void ath11k_debugfs_destroy(void) ++{ ++ debugfs_remove_recursive(debugfs_ath11k); ++ debugfs_ath11k = NULL; ++} ++ + void ath11k_debugfs_fw_stats_init(struct ath11k *ar) + { + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", +@@ -1675,6 +1693,9 @@ int ath11k_debugfs_register(struct ath11 + char pdev_name[10]; + char buf[100] = {0}; + ++ if (!(IS_ERR_OR_NULL(ar->debug.debugfs_pdev))) ++ return 0; ++ + snprintf(pdev_name, sizeof(pdev_name), "%s%u", "mac", ar->pdev_idx); + + ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc); +@@ -1752,6 +1773,9 @@ void ath11k_debugfs_unregister(struct at + kfree(dbr_debug); + ar->debug.dbr_debug[i] = NULL; + } ++ ++ debugfs_remove_recursive(ar->debug.debugfs_pdev); ++ ar->debug.debugfs_pdev = NULL; + } + + static ssize_t ath11k_write_twt_add_dialog(struct file *file, +--- a/drivers/net/wireless/ath/ath11k/debugfs.h ++++ b/drivers/net/wireless/ath/ath11k/debugfs.h +@@ -263,6 +263,8 @@ struct ath11k_fw_dbglog { + }; + + #ifdef CPTCFG_ATH11K_DEBUGFS ++int ath11k_debugfs_create(void); ++void ath11k_debugfs_destroy(void); + int ath11k_debugfs_soc_create(struct ath11k_base *ab); + void ath11k_debugfs_soc_destroy(struct ath11k_base *ab); + int ath11k_debugfs_pdev_create(struct ath11k_base *ab); +@@ -314,6 +316,15 @@ void ath11k_debugfs_add_dbring_entry(str + struct hal_srng *srng); + + #else ++static inline int ath11k_debugfs_create(void) ++{ ++ return 0; ++} ++ ++static inline void ath11k_debugfs_destroy(void) ++{ ++} ++ + static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab) + { + return 0; diff --git a/package/kernel/mac80211/patches/nss/ath11k/188-ath11k-m3-ssr-dump-collection.patch b/package/kernel/mac80211/patches/nss/ath11k/188-ath11k-m3-ssr-dump-collection.patch new file mode 100644 index 00000000000000..1fe5317105c137 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/188-ath11k-m3-ssr-dump-collection.patch @@ -0,0 +1,12 @@ +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2100,6 +2100,9 @@ static int ath11k_qmi_assign_target_mem_ + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; ++ case M3_DUMP_REGION_TYPE: ++ idx++; ++ break; + default: + ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", + ab->qmi.target_mem[i].type); diff --git a/package/kernel/mac80211/patches/nss/ath11k/191-ath11k-add-mgmt-and-data-ack-rssi.patch b/package/kernel/mac80211/patches/nss/ath11k/191-ath11k-add-mgmt-and-data-ack-rssi.patch new file mode 100644 index 00000000000000..466dacd1006768 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/191-ath11k-add-mgmt-and-data-ack-rssi.patch @@ -0,0 +1,21 @@ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -9580,6 +9580,8 @@ static int __ath11k_mac_register(struct + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + ++ wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); ++ + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -4065,6 +4065,7 @@ ath11k_wmi_copy_resource_config(struct w + wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters; + wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id; + wmi_cfg->flag1 = tg_cfg->flag1; ++ wmi_cfg->flag1 |= WMI_RSRC_CFG_FLAG1_ACK_RSSI; + wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support; + wmi_cfg->sched_params = tg_cfg->sched_params; + wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; diff --git a/package/kernel/mac80211/patches/nss/ath11k/199-002-ath11k_nss-add-nss-driver-interface.patch b/package/kernel/mac80211/patches/nss/ath11k/199-002-ath11k_nss-add-nss-driver-interface.patch new file mode 100644 index 00000000000000..851e4ed0225a95 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/199-002-ath11k_nss-add-nss-driver-interface.patch @@ -0,0 +1,3033 @@ +From 0fa55ca418c8afd6da242407a184c23548c553dc Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Fri, 5 Jun 2020 12:21:15 +0530 +Subject: [PATCH 2/3] ath11k: Add nss driver interface + +This patch adds interface support for accessing nss driver with +support for initialization, teardown, vap up/down, peer create/delete, +tx/rx. NSS Stats addition is not part of this version. + +Signed-off-by: Sriram R +--- + drivers/net/wireless/ath/ath11k/Kconfig | 9 + + drivers/net/wireless/ath/ath11k/Makefile | 1 + + drivers/net/wireless/ath/ath11k/nss.c | 1762 ++++++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/nss.h | 217 ++++ + 4 files changed, 1989 insertions(+) + create mode 100644 drivers/net/wireless/ath/ath11k/nss.c + create mode 100644 drivers/net/wireless/ath/ath11k/nss.h + +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -13,6 +13,16 @@ config ATH11K + + If you choose to build a module, it'll be called ath11k. + ++config ATH11K_NSS_SUPPORT ++ bool "QCA ath11k nss support" ++ depends on ATH11K ++ default n ++ select MAC80211_NSS_SUPPORT ++ ---help--- ++ Enables NSS offload support for ATH11K driver ++ ++ If unsure, say Y to enable NSS offload support. ++ + config ATH11K_AHB + tristate "Atheros ath11k AHB support" + depends on m +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -25,6 +25,7 @@ ath11k-$(CPTCFG_ATH11K_TRACING) += trace + ath11k-$(CPTCFG_ATH11K_THERMAL) += thermal.o + ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o + ath11k-$(CONFIG_PM) += wow.o ++ath11k-$(CPTCFG_ATH11K_NSS_SUPPORT) += nss.o + + obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o + ath11k_ahb-y += ahb.o +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -0,0 +1,2396 @@ ++// SPDX-License-Identifier: BSD-3-Clause-Clear ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. ++ */ ++ ++#include "debug.h" ++#include "mac.h" ++#include "nss.h" ++#include "core.h" ++#include "peer.h" ++#include "hif.h" ++#include "wmi.h" ++#include "../../../../../net/mac80211/sta_info.h" ++ ++/*-----------------------------ATH11K-NSS Helpers--------------------------*/ ++ ++static enum ath11k_nss_opmode ++ath11k_nss_get_vdev_opmode(struct ath11k_vif *arvif) ++{ ++ switch (arvif->vdev_type) { ++ case WMI_VDEV_TYPE_AP: ++ return ATH11K_NSS_OPMODE_AP; ++ case WMI_VDEV_TYPE_STA: ++ return ATH11K_NSS_OPMODE_STA; ++ default: ++ ath11k_warn(arvif->ar->ab, "unsupported nss vdev type %d\n", ++ arvif->vdev_type); ++ } ++ ++ return ATH11K_NSS_OPMODE_UNKNOWN; ++} ++ ++static void ath11k_nss_wifili_stats_sync(struct ath11k_base *ab, ++ struct nss_wifili_stats_sync_msg *wlsoc_stats) ++{ ++ struct nss_wifili_device_stats *devstats = &wlsoc_stats->stats; ++ struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats; ++ int i; ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ soc_stats->err_ring_pkts += devstats->rxwbm_stats.err_src_rxdma; ++ soc_stats->invalid_rbm += devstats->rxwbm_stats.invalid_buf_mgr; ++ ++ for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) ++ soc_stats->rxdma_error[i] += devstats->rxwbm_stats.err_dma_codes[i]; ++ ++ for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) ++ soc_stats->reo_error[i] += devstats->rxwbm_stats.err_reo_codes[i]; ++ ++ for (i = 0; i < DP_REO_DST_RING_MAX; i++) ++ soc_stats->hal_reo_error[i] += devstats->rxreo_stats[i].ring_error; ++ ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) ++ soc_stats->tx_err.desc_na[i] += devstats->tcl_stats[i].tcl_ring_full; ++ ++ ++ for (i = 0; i < NSS_WIFILI_MAX_TCL_DATA_RINGS_MSG; i++) ++ atomic_add(devstats->txcomp_stats[i].invalid_bufsrc ++ + devstats->txcomp_stats[i].invalid_cookie ++ + devstats->tx_sw_pool_stats[i].desc_alloc_fail ++ + devstats->tx_ext_sw_pool_stats[i].desc_alloc_fail, ++ &soc_stats->tx_err.misc_fail); ++ ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) ++ atomic_add(devstats->tx_data_stats[i].tx_send_fail_cnt, ++ &soc_stats->tx_err.misc_fail); ++ ++ spin_unlock_bh(&ab->base_lock); ++} ++ ++static void ath11k_nss_get_peer_stats(struct ath11k_base *ab, struct nss_wifili_peer_stats *stats) ++{ ++ struct ath11k_peer *peer; ++ struct nss_wifili_peer_ctrl_stats *pstats = NULL; ++ int i, j; ++ u64 tx_packets, tx_bytes, tx_dropped = 0; ++ u64 rx_packets, rx_bytes, rx_dropped; ++ ++ if (!ab->nss.stats_enabled) ++ return; ++ ++ for (i = 0; i < stats->npeers; i++) { ++ pstats = &stats->wpcs[i]; ++ ++ rcu_read_lock(); ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_id(ab, pstats->peer_id); ++ if (!peer || !peer->sta) { ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss wifili: unable to find peer %d\n", pstats->peer_id); ++ spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++ continue; ++ } ++ ++ if (!peer->nss.nss_stats) { ++ spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++ return; ++ } ++ ++ if (pstats->tx.tx_success_cnt) ++ peer->nss.nss_stats->last_ack = jiffies; ++ ++ if (pstats->rx.rx_recvd) { ++ peer->nss.nss_stats->last_rx = jiffies; ++ } ++ ++ tx_packets = pstats->tx.tx_mcast_cnt + ++ pstats->tx.tx_ucast_cnt + ++ pstats->tx.tx_bcast_cnt; ++ peer->nss.nss_stats->tx_packets += tx_packets; ++ tx_bytes = pstats->tx.tx_mcast_bytes + ++ pstats->tx.tx_ucast_bytes + ++ pstats->tx.tx_bcast_bytes; ++ peer->nss.nss_stats->tx_bytes += tx_bytes; ++ peer->nss.nss_stats->tx_retries += pstats->tx.retries; ++ ++ for (j = 0; j < NSS_WIFILI_TQM_RR_MAX; j++) ++ tx_dropped += pstats->tx.dropped.drop_stats[j]; ++ ++ peer->nss.nss_stats->tx_failed += tx_dropped; ++ ++ ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->vif, tx_bytes, tx_packets); ++ ++ rx_packets = pstats->rx.rx_recvd; ++ peer->nss.nss_stats->rx_packets += rx_packets; ++ rx_bytes = pstats->rx.rx_recvd_bytes; ++ peer->nss.nss_stats->rx_bytes += rx_bytes; ++ rx_dropped = pstats->rx.err.mic_err + ++ pstats->rx.err.decrypt_err; ++ peer->nss.nss_stats->rx_dropped += rx_dropped; ++ ++ ATH11K_NSS_TXRX_NETDEV_STATS(rx, peer->vif, rx_bytes, rx_packets); ++ ++ spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++ } ++} ++ ++void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter) ++{ ++ if (ab->nss.enabled) ++ tlv_filter->rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; ++} ++ ++static u32 ath11k_nss_cipher_type(struct ath11k_base *ab, u32 cipher) ++{ ++ switch (cipher) { ++ case WLAN_CIPHER_SUITE_CCMP: ++ return PEER_SEC_TYPE_AES_CCMP; ++ case WLAN_CIPHER_SUITE_TKIP: ++ return PEER_SEC_TYPE_TKIP; ++ case WLAN_CIPHER_SUITE_CCMP_256: ++ return PEER_SEC_TYPE_AES_CCMP_256; ++ case WLAN_CIPHER_SUITE_GCMP: ++ return PEER_SEC_TYPE_AES_GCMP; ++ case WLAN_CIPHER_SUITE_GCMP_256: ++ return PEER_SEC_TYPE_AES_GCMP_256; ++ default: ++ ath11k_warn(ab, "unknown cipher type %d\n", cipher); ++ return PEER_SEC_TYPE_NONE; ++ } ++} ++ ++static void ath11k_nss_tx_encap_nwifi(struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ u8 *qos_ctl; ++ ++ if (!ieee80211_is_data_qos(hdr->frame_control)) ++ return; ++ ++ qos_ctl = ieee80211_get_qos_ctl(hdr); ++ memmove(skb->data + IEEE80211_QOS_CTL_LEN, ++ skb->data, (void *)qos_ctl - (void *)skb->data); ++ skb_pull(skb, IEEE80211_QOS_CTL_LEN); ++ ++ hdr = (void *)skb->data; ++ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); ++} ++ ++static void ath11k_nss_tx_encap_raw(struct sk_buff *skb) ++{ ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ struct ieee80211_hdr *hdr = (void *)skb->data; ++ u32 cipher; ++ ++ if (!ieee80211_has_protected(hdr->frame_control) || !info->control.hw_key) ++ return; ++ ++ /* Include length for MIC */ ++ skb_put(skb, IEEE80211_CCMP_MIC_LEN); ++ ++ /* Include length for ICV if TKIP is used */ ++ cipher = info->control.hw_key->cipher; ++ if (cipher == WLAN_CIPHER_SUITE_TKIP) ++ skb_put(skb, IEEE80211_TKIP_ICV_LEN); ++} ++ ++static void ath11k_nss_peer_mem_free(struct ath11k_base *ab, u32 peer_id) ++{ ++ struct ath11k_peer *peer; ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ if(ab->nss.debug_mode) ++ ath11k_warn(ab, "ath11k_nss: unable to free peer mem, peer_id:%d\n", ++ peer_id); ++ return; ++ } ++ ++ dma_unmap_single(ab->dev, peer->nss.paddr, ++ WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE); ++ ++ kfree(peer->nss.vaddr); ++ if (peer->nss.nss_stats) { ++ kfree(peer->nss.nss_stats); ++ peer->nss.nss_stats = NULL; ++ } ++ ++ complete(&peer->nss.complete); ++ spin_unlock_bh(&ab->base_lock); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer %d mem freed\n", peer_id); ++} ++ ++/*-----------------------------Events/Callbacks------------------------------*/ ++ ++void ath11k_nss_wifili_event_receive(struct ath11k_base *ab, struct nss_wifili_msg *msg) ++{ ++ u32 msg_type = msg->cm.type; ++ enum nss_cmn_response response = msg->cm.response; ++ u32 error = msg->cm.error; ++ u32 peer_id; ++ struct nss_wifili_peer_stats *peer_stats; ++ ++ if (!ab) ++ return; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss wifili event received %d response %d error %d\n", ++ msg_type, response, error); ++ ++ switch (msg_type) { ++ case NSS_WIFILI_INIT_MSG: ++ case NSS_WIFILI_PDEV_INIT_MSG: ++ case NSS_WIFILI_START_MSG: ++ case NSS_WIFILI_SOC_RESET_MSG: ++ case NSS_WIFILI_STOP_MSG: ++ case NSS_WIFILI_PDEV_DEINIT_MSG: ++ ab->nss.response = response; ++ complete(&ab->nss.complete); ++ break; ++ ++ case NSS_WIFILI_PEER_CREATE_MSG: ++ if (response != NSS_CMN_RESPONSE_EMSG) ++ break; ++ ++ peer_id = (&msg->msg.peermsg)->peer_id; ++ ++ /* free peer memory allocated during peer create due to failure */ ++ ath11k_nss_peer_mem_free(ab, peer_id); ++ break; ++ case NSS_WIFILI_PEER_DELETE_MSG: ++ peer_id = (&msg->msg.peermsg)->peer_id; ++ ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab, "ath11k_nss: peer delete failed%d\n", ++ peer_id); ++ ++ /* free peer memory allocated during peer create irrespective of ++ * delete status ++ */ ++ ath11k_nss_peer_mem_free(ab, peer_id); ++ break; ++ case NSS_WIFILI_PEER_SECURITY_TYPE_MSG: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab, "peer securty config failed\n"); ++ ++ break; ++ case NSS_WIFILI_PEER_UPDATE_AUTH_FLAG: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab, "peer authorize config failed\n"); ++ ++ break; ++ case NSS_WIFILI_STATS_MSG: ++ if (response == NSS_CMN_RESPONSE_EMSG) { ++ ath11k_warn(ab, "soc_dp_stats failed to get updated\n"); ++ break; ++ } ++ ath11k_nss_wifili_stats_sync(ab, &msg->msg.wlsoc_stats); ++ break; ++ case NSS_WIFILI_PEER_STATS_MSG: ++ peer_stats = &msg->msg.peer_stats.stats; ++ if (response == NSS_CMN_RESPONSE_EMSG) { ++ ath11k_warn(ab, "peer stats msg failed with error = %u\n", error); ++ break; ++ } ++ ath11k_nss_get_peer_stats(ab, peer_stats); ++ break; ++ case NSS_WIFILI_TID_REOQ_SETUP_MSG: ++ /* TODO setup tidq */ ++ break; ++ default: ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type); ++ break; ++ } ++} ++ ++void ath11k_nss_process_mic_error(struct ath11k_base *ab, struct sk_buff *skb) ++{ ++ struct ath11k_vif *arvif; ++ struct ath11k_peer *peer = NULL; ++ struct hal_rx_desc *desc = (struct hal_rx_desc *)skb->data; ++ struct wireless_dev *wdev; ++ u16 peer_id; ++ u8 peer_addr[ETH_ALEN]; ++ u8 ucast_keyidx, mcast_keyidx; ++ bool is_mcbc; ++ ++ if (!ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc)) ++ goto fail; ++ ++ is_mcbc = ath11k_dp_rx_h_attn_is_mcbc(ab, desc); ++ peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ab, desc); ++ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ if (!peer) { ++ ath11k_info(ab, "ath11k_nss:peer not found"); ++ spin_unlock_bh(&ab->base_lock); ++ goto fail; ++ } ++ ++ if (!peer->vif) { ++ ath11k_warn(ab, "ath11k_nss:vif not found"); ++ spin_unlock_bh(&ab->base_lock); ++ goto fail; ++ } ++ ++ ether_addr_copy(peer_addr, peer->addr); ++ mcast_keyidx = peer->mcast_keyidx; ++ ucast_keyidx = peer->ucast_keyidx; ++ arvif = ath11k_vif_to_arvif(peer->vif); ++ spin_unlock_bh(&ab->base_lock); ++ ++ if (!arvif->is_started) { ++ ath11k_warn(ab, "ath11k_nss:arvif not started"); ++ goto fail; ++ } ++ ++ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif); ++ if (!wdev) { ++ ath11k_warn(ab, "ath11k_nss: wdev is null\n"); ++ goto fail; ++ } ++ ++ if (!wdev->netdev) { ++ ath11k_warn(ab, "ath11k_nss: netdev is null\n"); ++ goto fail; ++ } ++ ++ cfg80211_michael_mic_failure(wdev->netdev, peer_addr, ++ is_mcbc ? NL80211_KEYTYPE_GROUP : ++ NL80211_KEYTYPE_PAIRWISE, ++ is_mcbc ? mcast_keyidx : ucast_keyidx, ++ NULL, GFP_ATOMIC); ++ dev_kfree_skb_any(skb); ++ return; ++ ++fail: ++ dev_kfree_skb_any(skb); ++ ath11k_warn(ab, "ath11k_nss: Failed to handle mic error\n"); ++ return; ++} ++ ++static void ++ath11k_nss_wifili_ext_callback_fn(struct ath11k_base *ab, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ struct nss_wifili_soc_per_packet_metadata *wepm; ++ ++ wepm = (struct nss_wifili_soc_per_packet_metadata *)(skb->head + ++ NSS_WIFILI_SOC_PER_PACKET_METADATA_OFFSET); ++ ++ switch (wepm->pkt_type) { ++ case NSS_WIFILI_SOC_EXT_DATA_PKT_MIC_ERROR: ++ ath11k_nss_process_mic_error(ab, skb); ++ break; ++ default: ++ kfree(skb); ++ break; ++ } ++} ++ ++void ath11k_nss_vdev_cfg_cb(void *app_data, struct nss_cmn_msg *msg) ++{ ++ struct ath11k_vif *arvif = (struct ath11k_vif *)app_data; ++ ++ if (!arvif) ++ return; ++ ++ ath11k_dbg(arvif->ar->ab, ATH11K_DBG_NSS, "vdev cfg msg callback received msg:%d rsp:%d\n", ++ msg->type, msg->response); ++ ++ complete(&arvif->nss.complete); ++} ++ ++static void ath11k_nss_vdev_event_receive(void *dev, struct nss_cmn_msg *vdev_msg) ++{ ++ /*TODO*/ ++} ++ ++static void ++ath11k_nss_vdev_special_data_receive(struct net_device *dev, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ /* TODO */ ++} ++ ++/* TODO: move to mac80211 after cleanups/refactoring required after feature completion */ ++static int ath11k_nss_deliver_rx(struct ieee80211_vif *vif, struct sk_buff *skb, ++ bool eth, int data_offs, struct napi_struct *napi) ++{ ++ struct sk_buff_head subframe_list; ++ struct ieee80211_hdr *hdr; ++ struct sk_buff *subframe; ++ struct net_device *dev; ++ int hdr_len; ++ u8 *qc; ++ ++ dev = skb->dev; ++ ++ if (eth) ++ goto deliver_msdu; ++ ++ hdr = (struct ieee80211_hdr *)skb->data; ++ hdr_len = ieee80211_hdrlen(hdr->frame_control); ++ ++ if (ieee80211_is_data_qos(hdr->frame_control)) { ++ qc = ieee80211_get_qos_ctl(hdr); ++ if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT) ++ goto deliver_amsdu; ++ } ++ ++ if (ieee80211_data_to_8023_exthdr(skb, NULL, vif->addr, vif->type, ++ data_offs - hdr_len, false)) { ++ dev_kfree_skb_any(skb); ++ return -EINVAL; ++ } ++ ++deliver_msdu: ++ skb->protocol = eth_type_trans(skb, dev); ++ napi_gro_receive(napi, skb); ++ return 0; ++ ++deliver_amsdu: ++ /* Move to the start of the first subframe */ ++ skb_pull(skb, data_offs); ++ ++ __skb_queue_head_init(&subframe_list); ++ ++ /* create list containing all the subframes */ ++ ieee80211_amsdu_to_8023s(skb, &subframe_list, NULL, ++ vif->type, 0, NULL, NULL); ++ ++ /* This shouldn't happen, indicating error during defragmentation */ ++ if (skb_queue_empty(&subframe_list)) ++ return -EINVAL; ++ ++ while (!skb_queue_empty(&subframe_list)) { ++ subframe = __skb_dequeue(&subframe_list); ++ subframe->protocol = eth_type_trans(subframe, dev); ++ napi_gro_receive(napi, subframe); ++ } ++ ++ return 0; ++} ++ ++static int ath11k_nss_undecap_raw(struct ath11k_vif *arvif, struct sk_buff *skb, ++ int *data_offset) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct ath11k *ar = arvif->ar; ++ enum hal_encrypt_type enctype; ++ struct ath11k_peer *peer = NULL; ++ struct ieee80211_hdr *hdr; ++ int hdr_len; ++ ++ hdr = (struct ieee80211_hdr *)skb->data; ++ hdr_len = ieee80211_hdrlen(hdr->frame_control); ++ ++ *data_offset = hdr_len; ++ ++ /* FCS is included in the raw mode skb, we can trim it, fcs error ++ * packets are not expected to be received in this path ++ */ ++ skb_trim(skb, skb->len - FCS_LEN); ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_addr(ab, hdr->addr2); ++ if (!peer) { ++ ath11k_warn(ab, "peer not found for raw/nwifi undecap, drop this packet\n"); ++ spin_unlock_bh(&ab->base_lock); ++ return -EINVAL; ++ } ++ enctype = peer->sec_type; ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ *data_offset += ath11k_dp_rx_crypto_param_len(ar, enctype); ++ ++ /* Strip ICV, MIC, MMIC */ ++ skb_trim(skb, skb->len - ++ ath11k_dp_rx_crypto_mic_len(ar, enctype)); ++ ++ skb_trim(skb, skb->len - ++ ath11k_dp_rx_crypto_icv_len(ar, enctype)); ++ ++ if (enctype == HAL_ENCRYPT_TYPE_TKIP_MIC) ++ skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN); ++ ++ return 0; ++} ++ ++static int ath11k_nss_undecap_nwifi(struct ath11k_vif *arvif, struct sk_buff *skb, ++ int *data_offset) ++{ ++ struct ieee80211_hdr *hdr; ++ int hdr_len; ++ ++ hdr = (struct ieee80211_hdr *)skb->data; ++ hdr_len = ieee80211_hdrlen(hdr->frame_control); ++ ++ *data_offset = hdr_len; ++ ++ /* We dont receive the IV from nss host on slow path ++ * hence we can return only the header length as offset. ++ **/ ++ return 0; ++} ++ ++static void ++ath11k_nss_vdev_data_receive(struct net_device *dev, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ enum ath11k_hw_txrx_mode decap_type; ++ struct wireless_dev *wdev = NULL; ++ struct ieee80211_vif *vif = NULL; ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ bool eth_decap = false; ++ int data_offs = 0; ++ int ret; ++ ++ if (!dev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ wdev = dev->ieee80211_ptr; ++ if (!wdev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ arvif = (struct ath11k_vif *)vif->drv_priv; ++ if (!arvif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ /* log the original skb received from nss */ ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss: ", ++ skb->data, skb->len); ++ ++ decap_type = arvif->nss.decap; ++ ++ switch (decap_type) { ++ case ATH11K_HW_TXRX_RAW: ++ ret = ath11k_nss_undecap_raw(arvif, skb, &data_offs); ++ break; ++ case ATH11K_HW_TXRX_NATIVE_WIFI: ++ ret = ath11k_nss_undecap_nwifi(arvif, skb, &data_offs); ++ break; ++ case ATH11K_HW_TXRX_ETHERNET: ++ /* no changes required for ethernet decap */ ++ ret = 0; ++ eth_decap = true; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret) { ++ ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", decap_type, ++ ret); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++} ++ ++int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb) ++{ ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); ++ struct ath11k_soc_dp_stats *soc_stats = &ar->ab->soc_stats; ++ ++ if (encap_type != arvif->nss.encap) { ++ ath11k_warn(ar->ab, "encap mismatch in nss tx skb encap type %d" \ ++ " vif encap type %d\n", encap_type, arvif->nss.encap); ++ goto drop; ++ } ++ ++ if (encap_type == HAL_TCL_ENCAP_TYPE_ETHERNET) ++ goto send; ++ ++ if (encap_type == HAL_TCL_ENCAP_TYPE_RAW) ++ ath11k_nss_tx_encap_raw(skb); ++ else ++ ath11k_nss_tx_encap_nwifi(skb); ++ ++send: ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "", "nss tx msdu: ", ++ skb->data, skb->len); ++ ++ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, arvif->nss.if_num); ++ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_dbg(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX), ++ "nss tx failure: %d\n", status); ++ atomic_inc(&soc_stats->tx_err.nss_tx_fail); ++ } ++ ++ return status; ++drop: ++ atomic_inc(&soc_stats->tx_err.misc_fail); ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++} ++ ++int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val) ++{ ++ struct nss_wifi_vdev_msg *vdev_msg = NULL; ++ struct nss_wifi_vdev_cmd_msg *vdev_cmd; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ /* Monitor interface is not offloaded to nss */ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ return 0; ++ ++ vdev_msg = kzalloc(sizeof(*vdev_msg), GFP_ATOMIC); ++ if (!vdev_msg) ++ return -ENOMEM; ++ ++ /* TODO: Convert to function for conversion in case of many ++ * such commands ++ */ ++ if (cmd == NSS_WIFI_VDEV_SECURITY_TYPE_CMD) ++ val = ath11k_nss_cipher_type(ar->ab, val); ++ ++ if (cmd == NSS_WIFI_VDEV_ENCAP_TYPE_CMD) ++ arvif->nss.encap = val; ++ else if (cmd == NSS_WIFI_VDEV_DECAP_TYPE_CMD) ++ arvif->nss.decap = val; ++ ++ vdev_cmd = &vdev_msg->msg.vdev_cmd; ++ vdev_cmd->cmd = cmd; ++ vdev_cmd->value = val; ++ ++ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_VDEV_INTERFACE_CMD_MSG, ++ sizeof(struct nss_wifi_vdev_cmd_msg), ++ NULL, NULL); ++ ++ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss vdev set cmd failure cmd:%d val:%d", ++ cmd, val); ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev set cmd success cmd:%d val:%d\n", ++ cmd, val); ++free: ++ kfree(vdev_msg); ++ return status; ++} ++ ++static int ath11k_nss_vdev_configure(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct nss_wifi_vdev_msg *vdev_msg; ++ struct nss_wifi_vdev_config_msg *vdev_cfg; ++ nss_tx_status_t status; ++ int ret; ++ ++ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); ++ if (!vdev_msg) ++ return -ENOMEM; ++ ++ vdev_cfg = &vdev_msg->msg.vdev_config; ++ ++ vdev_cfg->radio_ifnum = ar->nss.if_num; ++ vdev_cfg->vdev_id = arvif->vdev_id; ++ ++ vdev_cfg->opmode = ath11k_nss_get_vdev_opmode(arvif); ++ ++ ether_addr_copy(vdev_cfg->mac_addr, arvif->vif->addr); ++ ++ reinit_completion(&arvif->nss.complete); ++ ++ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_VDEV_INTERFACE_CONFIGURE_MSG, ++ sizeof(struct nss_wifi_vdev_config_msg), ++ (nss_wifi_vdev_msg_callback_t *)ath11k_nss_vdev_cfg_cb, ++ arvif); ++ ++ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "failed to configure nss vdev nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&arvif->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ar->ab, "timeout in receiving nss vdev cfg response\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(vdev_msg); ++ ++ return ret; ++} ++ ++static void ath11k_nss_vdev_unregister(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ ++ switch (arvif->vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_STATION: ++ nss_unregister_wifi_vdev_if(arvif->nss.if_num); ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "unregistered nss vdev %d \n", ++ arvif->nss.if_num); ++ break; ++ default: ++ ath11k_warn(ab, "unsupported interface type %d for nss vdev unregister\n", ++ arvif->vif->type); ++ return; ++ } ++} ++ ++static int ath11k_nss_vdev_register(struct ath11k_vif *arvif, ++ struct net_device *netdev) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ nss_tx_status_t status; ++ u32 features = 0; ++ ++ switch (arvif->vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_STATION: ++ status = nss_register_wifi_vdev_if(ar->nss.ctx, ++ arvif->nss.if_num, ++ ath11k_nss_vdev_data_receive, ++ ath11k_nss_vdev_special_data_receive, ++ ath11k_nss_vdev_event_receive, ++ netdev, features); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failed to register nss vdev if_num %d nss_err:%d\n", ++ arvif->nss.if_num, status); ++ return -EINVAL; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "registered nss vdev if_num %d\n", ++ arvif->nss.if_num); ++ ++ break; ++ default: ++ ath11k_warn(ab, "unsupported interface type %d for nss vdev register\n", ++ arvif->vif->type); ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ ++void ath11k_nss_vdev_free(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ nss_tx_status_t status; ++ ++ switch (arvif->vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_STATION: ++ status = nss_dynamic_interface_dealloc_node( ++ arvif->nss.if_num, ++ NSS_DYNAMIC_INTERFACE_TYPE_VAP); ++ if (status != NSS_TX_SUCCESS) ++ ath11k_warn(ab, "failed to free nss vdev nss_err:%d\n", ++ status); ++ else ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "nss vdev interface deallocated\n"); ++ ++ return; ++ default: ++ ath11k_warn(ab, "unsupported interface type %d for nss vdev dealloc\n", ++ arvif->vif->type); ++ return; ++ } ++} ++ ++static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ enum nss_dynamic_interface_type if_type; ++ int if_num; ++ ++ /* Initialize completion for verifying NSS message response */ ++ init_completion(&arvif->nss.complete); ++ ++ switch (arvif->vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_STATION: ++ if_type = NSS_DYNAMIC_INTERFACE_TYPE_VAP; ++ /* allocate interface context with NSS driver for the new vdev */ ++ if_num = nss_dynamic_interface_alloc_node(if_type); ++ if (if_num < 0) { ++ ath11k_warn(ab, "failed to allocate nss vdev\n"); ++ return -EINVAL; ++ } ++ ++ arvif->nss.if_num = if_num; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated nss vdev if_num %d\n", ++ arvif->nss.if_num); ++ ++ break; ++ default: ++ ath11k_warn(ab, "unsupported interface type %d for nss vdev alloc\n", ++ arvif->vif->type); ++ return -ENOTSUPP; ++ } ++ ++ return 0; ++} ++ ++int ath11k_nss_vdev_create(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ struct wireless_dev *wdev; ++ int ret; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ /* Monitor interface is not offloaded to nss */ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ return 0; ++ ++ if (arvif->nss.created) ++ return 0; ++ ++ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif); ++ if (!wdev) { ++ ath11k_warn(ab, "ath11k_nss: wdev is null\n"); ++ return -EINVAL; ++ } ++ ++ if (!wdev->netdev) { ++ ath11k_warn(ab, "ath11k_nss: netdev is null\n"); ++ return -EINVAL; ++ } ++ ++ ret = ath11k_nss_vdev_alloc(arvif); ++ if (ret) ++ return ret; ++ ++ ret = ath11k_nss_vdev_register(arvif, wdev->netdev); ++ if (ret) ++ goto free_vdev; ++ ++ switch (arvif->vif->type) { ++ case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_STATION: ++ ret = ath11k_nss_vdev_configure(arvif); ++ if (ret) ++ goto unregister_vdev; ++ ++ break; ++ default: ++ ret = -ENOTSUPP; ++ goto unregister_vdev; ++ } ++ ++ arvif->nss.created = true; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "nss vdev interface created ctx %pK, ifnum %d\n", ++ ar->nss.ctx, arvif->nss.if_num); ++ ++ return ret; ++ ++unregister_vdev: ++ ath11k_nss_vdev_unregister(arvif); ++free_vdev: ++ ath11k_nss_vdev_free(arvif); ++ ++ return ret; ++} ++ ++void ath11k_nss_vdev_delete(struct ath11k_vif *arvif) ++{ ++ if (!arvif->ar->ab->nss.enabled) ++ return; ++ ++ /* Monitor interface is not offloaded to nss */ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ return; ++ ++ if (!arvif->nss.created) ++ return; ++ ++ ath11k_nss_vdev_unregister(arvif); ++ ++ ath11k_nss_vdev_free(arvif); ++ ++ arvif->nss.created = false; ++} ++ ++int ath11k_nss_vdev_up(struct ath11k_vif *arvif) ++{ ++ struct nss_wifi_vdev_msg *vdev_msg = NULL; ++ struct nss_wifi_vdev_enable_msg *vdev_en; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ /* Monitor interface is not offloaded to nss */ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ return 0; ++ ++ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); ++ if (!vdev_msg) ++ return -ENOMEM; ++ ++ vdev_en = &vdev_msg->msg.vdev_enable; ++ ++ ether_addr_copy(vdev_en->mac_addr, arvif->vif->addr); ++ ++ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_VDEV_INTERFACE_UP_MSG, ++ sizeof(struct nss_wifi_vdev_enable_msg), ++ NULL, NULL); ++ ++ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss vdev up tx msg error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev up tx msg success\n"); ++free: ++ kfree(vdev_msg); ++ return ret; ++} ++ ++int ath11k_nss_vdev_down(struct ath11k_vif *arvif) ++{ ++ struct nss_wifi_vdev_msg *vdev_msg = NULL; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ /* Monitor interface is not offloaded to nss */ ++ if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ++ return 0; ++ ++ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); ++ if (!vdev_msg) ++ return -ENOMEM; ++ ++ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_VDEV_INTERFACE_DOWN_MSG, ++ sizeof(struct nss_wifi_vdev_disable_msg), ++ NULL, NULL); ++ ++ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss vdev down tx msg error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev down tx msg success\n"); ++free: ++ kfree(vdev_msg); ++ return ret; ++} ++ ++/*----------------------------Peer Setup/Config -----------------------------*/ ++ ++int ath11k_nss_set_peer_sec_type(struct ath11k *ar, ++ struct ath11k_peer *peer, ++ struct ieee80211_key_conf *key_conf) ++{ ++ struct nss_wifili_peer_security_type_msg *sec_msg; ++ nss_wifili_msg_callback_t msg_cb; ++ struct nss_wifili_msg *wlmsg; ++ nss_tx_status_t status; ++ u8 *mic_key; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ sec_msg = &wlmsg->msg.securitymsg; ++ sec_msg->peer_id = peer->peer_id; ++ ++ /* 0 -unicast , 1 - mcast/unicast */ ++ sec_msg->pkt_type = !(key_conf->flags & IEEE80211_KEY_FLAG_PAIRWISE); ++ ++ sec_msg->security_type = ath11k_nss_cipher_type(ar->ab, ++ key_conf->cipher); ++ ++ if (sec_msg->security_type == PEER_SEC_TYPE_TKIP) { ++ mic_key = &key_conf->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]; ++ memcpy(&sec_msg->mic_key[0], mic_key, NSS_WIFILI_MIC_KEY_LEN); ++ } ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, ++ NSS_WIFILI_PEER_SECURITY_TYPE_MSG, ++ sizeof(struct nss_wifili_peer_security_type_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss peer %d security cfg fail %d\n", ++ peer->peer_id, status); ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d security cfg complete\n", ++ peer->peer_id); ++free: ++ kfree(wlmsg); ++ return status; ++} ++ ++int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id) ++{ ++ struct nss_wifili_peer_update_auth_flag *auth_msg; ++ nss_wifili_msg_callback_t msg_cb; ++ struct nss_wifili_msg *wlmsg; ++ nss_tx_status_t status; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ auth_msg = &wlmsg->msg.peer_auth; ++ auth_msg->peer_id = peer_id; ++ auth_msg->auth_flag = 1; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, ++ NSS_WIFILI_PEER_UPDATE_AUTH_FLAG, ++ sizeof(struct nss_wifili_peer_update_auth_flag), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss peer %d auth cfg fail %d\n", ++ peer_id, status); ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d auth cfg complete\n", ++ peer_id); ++free: ++ kfree(wlmsg); ++ return status; ++} ++ ++void ath11k_nss_update_sta_stats(struct station_info *sinfo, ++ struct ieee80211_sta *sta, ++ struct ath11k_sta *arsta) ++{ ++ struct sta_info *stainfo; ++ struct ath11k_peer *peer; ++ int tid_idx; ++ struct ath11k *ar = arsta->arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (!ab->nss.enabled) ++ return; ++ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_addr(arsta->arvif->ar->ab, sta->addr); ++ if (!peer) { ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %pM\n", ++ sta->addr); ++ goto exit; ++ } ++ ++ if (!peer->nss.nss_stats) ++ goto exit; ++ ++ stainfo = container_of(sta, struct sta_info, sta); ++ if (peer->nss.nss_stats->last_rx && ++ time_after((unsigned long)peer->nss.nss_stats->last_rx, stainfo->deflink.rx_stats.last_rx)) ++ stainfo->deflink.rx_stats.last_rx = peer->nss.nss_stats->last_rx; ++ ++ if (peer->nss.nss_stats->last_ack && ++ time_after((unsigned long)peer->nss.nss_stats->last_ack, stainfo->deflink.status_stats.last_ack)) ++ stainfo->deflink.status_stats.last_ack = peer->nss.nss_stats->last_ack; ++ ++ stainfo->deflink.rx_stats.dropped += peer->nss.nss_stats->rx_dropped - ++ peer->nss.nss_stats->last_rxdrop; ++ peer->nss.nss_stats->last_rxdrop = peer->nss.nss_stats->rx_dropped; ++ ++ sinfo->tx_packets = 0; ++ /* Add only ac-0 count as mgmt packets uses WME_AC_BE */ ++ sinfo->tx_packets += stainfo->deflink.tx_stats.packets[WME_AC_BE]; ++ sinfo->tx_packets += peer->nss.nss_stats->tx_packets; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); ++ sinfo->tx_bytes = 0; ++ ++ /* Add only ac-0 count as mgmt packets uses WME_AC_BE */ ++ sinfo->tx_bytes += stainfo->deflink.tx_stats.bytes[WME_AC_BE]; ++ sinfo->tx_bytes += peer->nss.nss_stats->tx_bytes; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES); ++ ++ sinfo->tx_failed = stainfo->deflink.status_stats.retry_failed + ++ peer->nss.nss_stats->tx_failed; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); ++ ++ sinfo->tx_retries = stainfo->deflink.status_stats.retry_count + ++ peer->nss.nss_stats->tx_retries; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); ++ ++ sinfo->rx_packets = stainfo->deflink.rx_stats.packets + ++ peer->nss.nss_stats->rx_packets; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); ++ ++ sinfo->rx_bytes = stainfo->deflink.rx_stats.bytes + ++ peer->nss.nss_stats->rx_bytes; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); ++ ++ if (peer->nss.nss_stats->rxrate.legacy || peer->nss.nss_stats->rxrate.nss) { ++ if (peer->nss.nss_stats->rxrate.legacy) { ++ sinfo->rxrate.legacy = peer->nss.nss_stats->rxrate.legacy; ++ } else { ++ sinfo->rxrate.mcs = peer->nss.nss_stats->rxrate.mcs; ++ sinfo->rxrate.nss = peer->nss.nss_stats->rxrate.nss; ++ sinfo->rxrate.bw = peer->nss.nss_stats->rxrate.bw; ++ sinfo->rxrate.he_gi = peer->nss.nss_stats->rxrate.he_gi; ++ sinfo->rxrate.he_dcm = peer->nss.nss_stats->rxrate.he_dcm; ++ sinfo->rxrate.he_ru_alloc = peer->nss.nss_stats->rxrate.he_ru_alloc; ++ } ++ sinfo->rxrate.flags = peer->nss.nss_stats->rxrate.flags; ++ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); ++ } ++ ++exit: ++ spin_unlock_bh(&ab->base_lock); ++} ++ ++void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info, ++ struct ath11k_peer *peer, ++ struct hal_rx_user_status *user_stats) ++{ ++ struct ath11k_sta *arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ u16 ath11k_hal_rx_legacy_rates[] = ++ { 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 }; ++ u16 rate = 0; ++ u32 preamble_type; ++ u8 mcs, nss; ++ struct ath11k *ar = arsta->arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (!ab->nss.enabled) ++ return; ++ ++ if (!peer->nss.nss_stats) ++ return; ++ ++ if (user_stats) { ++ mcs = user_stats->mcs; ++ nss = user_stats->nss; ++ preamble_type = user_stats->preamble_type; ++ } else { ++ mcs = ppdu_info->mcs; ++ nss = ppdu_info->nss; ++ preamble_type = ppdu_info->preamble_type; ++ } ++ ++ if ((preamble_type == WMI_RATE_PREAMBLE_CCK || ++ preamble_type == WMI_RATE_PREAMBLE_OFDM) && ++ (ppdu_info->rate < ATH11K_LEGACY_NUM)) { ++ rate = ath11k_hal_rx_legacy_rates[ppdu_info->rate]; ++ } ++ ++ memset(&peer->nss.nss_stats->rxrate, 0, sizeof(peer->nss.nss_stats->rxrate)); ++ ++ switch (preamble_type) { ++ case WMI_RATE_PREAMBLE_OFDM: ++ peer->nss.nss_stats->rxrate.legacy = rate; ++ break; ++ case WMI_RATE_PREAMBLE_CCK: ++ peer->nss.nss_stats->rxrate.legacy = rate; ++ break; ++ case WMI_RATE_PREAMBLE_HT: ++ if (mcs >= ATH11K_HT_MCS_NUM) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, ++ "Received invalid mcs in HT mode %d\n", mcs); ++ return; ++ } ++ peer->nss.nss_stats->rxrate.mcs = mcs; ++ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_MCS; ++ if (ppdu_info->gi) ++ peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; ++ break; ++ case WMI_RATE_PREAMBLE_VHT: ++ if (mcs > ATH11K_VHT_MCS_MAX) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, ++ "Received invalid mcs in VHT mode %d\n", mcs); ++ return; ++ } ++ peer->nss.nss_stats->rxrate.mcs = mcs; ++ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS; ++ if (ppdu_info->gi) ++ peer->nss.nss_stats->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; ++ break; ++ case WMI_RATE_PREAMBLE_HE: ++ if (mcs > ATH11K_HE_MCS_MAX) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, ++ "Received invalid mcs in HE mode %d\n", mcs); ++ return; ++ } ++ peer->nss.nss_stats->rxrate.mcs = mcs; ++ peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_HE_MCS; ++ peer->nss.nss_stats->rxrate.he_dcm = ppdu_info->dcm; ++ peer->nss.nss_stats->rxrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(ppdu_info->gi); ++ peer->nss.nss_stats->rxrate.he_ru_alloc = ppdu_info->ru_alloc; ++ break; ++ } ++ ++ peer->nss.nss_stats->rxrate.nss = nss; ++ peer->nss.nss_stats->rxrate.bw = ath11k_mac_bw_to_mac80211_bw(ppdu_info->bw); ++} ++ ++int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr) ++{ ++ struct nss_wifili_peer_msg *peer_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ struct ath11k_peer *peer; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_by_addr(ab, addr); ++ if (!peer) { ++ ath11k_warn(ab, "peer (%pM) not found for nss peer delete\n", addr); ++ spin_unlock_bh(&ab->base_lock); ++ return -EINVAL; ++ } ++ ++ if (!peer->nss.vaddr) { ++ ath11k_warn(ab, "peer already deleted or peer create failed %pM\n", ++ addr); ++ spin_unlock_bh(&ab->base_lock); ++ return -EINVAL; ++ } ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) { ++ ath11k_warn(ab, "nss send peer delete msg alloc failure\n"); ++ ret = -ENOMEM; ++ goto free_peer; ++ } ++ ++ peer_msg = &wlmsg->msg.peermsg; ++ ++ peer_msg->vdev_id = peer->vdev_id; ++ peer_msg->peer_id = peer->peer_id; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_PEER_DELETE_MSG, ++ sizeof(struct nss_wifili_peer_msg), ++ msg_cb, NULL); ++ ++ reinit_completion(&peer->nss.complete); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send peer (%pM) delete msg tx error %d\n", ++ addr, status); ++ ret = -EINVAL; ++ kfree(wlmsg); ++ goto free_peer; ++ } else { ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer delete message success : peer_id %d\n", ++ peer->peer_id); ++ ret = 0; ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++ ++ kfree(wlmsg); ++ ++ /* No need to return failure or free up here, since the msg was tx succesfully ++ * the peer delete response would be received from NSS which will free up ++ * the allocated memory ++ */ ++ ret = wait_for_completion_timeout(&peer->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (ab->nss.debug_mode && !ret) ++ ath11k_warn(ab, "timeout while waiting for nss peer delete msg response\n"); ++ ++ return 0; ++ ++free_peer: ++ dma_unmap_single(ab->dev, peer->nss.paddr, ++ WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE); ++ kfree(peer->nss.vaddr); ++ if (peer->nss.nss_stats) { ++ kfree(peer->nss.nss_stats); ++ peer->nss.nss_stats = NULL; ++ } ++ spin_unlock_bh(&ab->base_lock); ++ return ret; ++} ++ ++int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer) ++{ ++ struct nss_wifili_peer_msg *peer_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret; ++ ++ if (!ab->nss.enabled) ++ return -ENOTSUPP; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ peer_msg = &wlmsg->msg.peermsg; ++ ++ peer_msg->vdev_id = peer->vdev_id; ++ peer_msg->peer_id = peer->peer_id; ++ peer_msg->hw_ast_idx = peer->hw_peer_id; ++ peer_msg->tx_ast_hash = peer->ast_hash; ++ ether_addr_copy(peer_msg->peer_mac_addr, peer->addr); ++ ++ peer->nss.vaddr = kzalloc(WIFILI_NSS_PEER_BYTE_SIZE, GFP_ATOMIC); ++ ++ /* Initialize completion for verifying Peer NSS message response */ ++ init_completion(&peer->nss.complete); ++ ++ if (!peer->nss.vaddr) { ++ ath11k_warn(ab, "failed to allocate memory for nss peer info\n"); ++ kfree(wlmsg); ++ return -ENOMEM; ++ } ++ ++ peer->nss.paddr = dma_map_single(ab->dev, peer->nss.vaddr, ++ WIFILI_NSS_PEER_BYTE_SIZE, DMA_TO_DEVICE); ++ ++ ret = dma_mapping_error(ab->dev, peer->nss.paddr); ++ if (ret) { ++ ath11k_warn(ab, "error during nss peer info memalloc\n"); ++ kfree(peer->nss.vaddr); ++ ret = -ENOMEM; ++ goto msg_free; ++ } ++ ++ peer_msg->nss_peer_mem = peer->nss.paddr; ++ peer_msg->psta_vdev_id = peer->vdev_id; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_PEER_CREATE_MSG, ++ sizeof(struct nss_wifili_peer_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ret = -EINVAL; ++ ath11k_warn(ab, "nss send peer (%pM) create msg tx error\n", ++ peer->addr); ++ goto peer_mem_free; ++ } ++ ++ ret = 0; ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "nss peer_create msg success mac:%pM vdev:%d peer_id:%d hw_ast_idx:%d ast_hash:%d\n", ++ peer_msg->peer_mac_addr, peer_msg->vdev_id, peer_msg->peer_id, ++ peer_msg->hw_ast_idx, peer_msg->tx_ast_hash); ++ ++ peer->nss.nss_stats = kzalloc(sizeof(*peer->nss.nss_stats), GFP_ATOMIC); ++ if (!peer->nss.nss_stats) { ++ ret = -ENOMEM; ++ ath11k_warn(ab, "Unable to create nss stats memory\n"); ++ goto peer_mem_free; ++ } ++ ++ goto msg_free; ++ ++peer_mem_free: ++ dma_unmap_single(ab->dev, peer->nss.paddr, ++ WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE); ++ kfree(peer->nss.vaddr); ++msg_free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++/*-------------------------------INIT/DEINIT---------------------------------*/ ++ ++static int ath11k_nss_radio_buf_cfg(struct ath11k *ar, int range, int buf_sz) ++{ ++ struct nss_wifili_radio_buf_cfg_msg *buf_cfg; ++ struct nss_wifili_radio_cfg_msg *radio_buf_cfg_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ radio_buf_cfg_msg = &wlmsg->msg.radiocfgmsg; ++ ++ radio_buf_cfg_msg->radio_if_num = ar->nss.if_num; ++ buf_cfg = &wlmsg->msg.radiocfgmsg.radiomsg.radiobufcfgmsg; ++ buf_cfg->range = range; ++ buf_cfg->buf_cnt = buf_sz; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, ++ NSS_WIFILI_RADIO_BUF_CFG, ++ sizeof(struct nss_wifili_radio_buf_cfg_msg), ++ NULL, NULL); ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss radio buf cfg send failed %d\n", status); ++ ret = -EINVAL; ++ } else { ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, ++ "nss radio cfg message range:%d buf_sz:%d if_num:%d ctx:%p\n", ++ range, buf_sz, ar->nss.if_num, ar->nss.ctx); ++ } ++ ++ kfree(wlmsg); ++ return ret; ++} ++ ++static void ath11k_nss_fill_srng_info(struct ath11k_base *ab, int ring_id, ++ struct nss_wifili_hal_srng_info *hsi) ++{ ++ struct ath11k_hal *hal = &ab->hal; ++ struct hal_srng *srng; ++ u32 offset; ++ int i; ++ ++ if (ring_id < 0) { ++ ath11k_warn(ab, "Invalid ring id used for nss init\n"); ++ WARN_ON(1); ++ return; ++ } ++ ++ srng = &hal->srng_list[ring_id]; ++ ++ hsi->ring_id = srng->ring_id; ++ hsi->ring_dir = srng->ring_dir; ++ hsi->ring_base_paddr = srng->ring_base_paddr; ++ hsi->entry_size = srng->entry_size; ++ hsi->num_entries = srng->num_entries; ++ hsi->flags = srng->flags; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Ring info to send to nss - ring_id:%d ring_dir:%d ring_paddr:%d entry_size:%d num_entries:%d flags:%d\n", ++ hsi->ring_id, hsi->ring_dir, hsi->ring_base_paddr, ++ hsi->entry_size, hsi->num_entries, hsi->flags); ++ ++ for (i = 0; i < HAL_SRNG_NUM_REG_GRP; i++) { ++ offset = srng->hwreg_base[i]; ++ ++ /* For PCI based devices, get the umac ring base address offset ++ * based on window register configuration. ++ */ ++ if (!(srng->flags & HAL_SRNG_FLAGS_LMAC_RING)) ++ offset = ath11k_hif_get_window_offset(ab, srng->hwreg_base[i]); ++ ++ hsi->hwreg_base[i] = (u32)ab->mem_pa + offset; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "SRNG Register %d address %d\n", ++ i, hsi->hwreg_base[i]); ++ } ++} ++ ++static void ath11k_nss_tx_desc_mem_free(struct ath11k_base *ab) ++{ ++ int i; ++ ++ for (i = 0; i < ATH11K_NSS_MAX_NUMBER_OF_PAGE; i++) { ++ if (!ab->nss.tx_desc_paddr[i]) ++ continue; ++ ++ dma_free_coherent(ab->dev, ++ ab->nss.tx_desc_size[i], ++ ab->nss.tx_desc_vaddr[i], ++ ab->nss.tx_desc_paddr[i]); ++ ab->nss.tx_desc_vaddr[i] = NULL; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated tx desc mem freed\n"); ++} ++ ++static int ath11k_nss_tx_desc_mem_alloc(struct ath11k_base *ab, u32 required_size, u32 *page_idx) ++{ ++ int i, alloc_size; ++ int curr_page_idx; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "tx desc mem alloc size: %d\n", required_size); ++ ++ curr_page_idx = *page_idx; ++ ++ for (i = 0, alloc_size = 0; i < required_size; i += alloc_size) { ++ alloc_size = required_size - i; ++ ++ if (alloc_size > WIFILI_NSS_MAX_MEM_PAGE_SIZE) ++ alloc_size = WIFILI_NSS_MAX_MEM_PAGE_SIZE; ++ ++ ab->nss.tx_desc_vaddr[curr_page_idx] = ++ dma_alloc_coherent(ab->dev, alloc_size, ++ &ab->nss.tx_desc_paddr[curr_page_idx], ++ GFP_KERNEL); ++ ++ if (!ab->nss.tx_desc_vaddr[curr_page_idx]) ++ return -ENOMEM; ++ ++ ab->nss.tx_desc_size[curr_page_idx] = alloc_size; ++ curr_page_idx++; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "curr page %d, allocated %d, total allocated %d\n", ++ curr_page_idx, alloc_size, i + alloc_size); ++ ++ if (curr_page_idx == ATH11K_NSS_MAX_NUMBER_OF_PAGE) { ++ ath11k_warn(ab, "max page number reached while tx desc mem allocation\n"); ++ return -EINVAL; ++ } ++ } ++ *page_idx = curr_page_idx; ++ return 0; ++} ++ ++static int ath11k_nss_fill_tx_desc_info(struct ath11k_base *ab, ++ struct nss_wifili_init_msg *wim) ++{ ++ struct nss_wifili_tx_desc_addtnl_mem_msg *dam; ++ u32 required_size, required_size_ext; ++ struct nss_wifili_tx_desc_init_msg *dim; ++ u32 tx_desc_limit_0 = 0; ++ u32 tx_desc_limit_1 = 0; ++ u32 tx_desc_limit_2 = 0; ++ u32 dam_page_idx = 0; ++ int page_idx = 0; ++ int i; ++ ++ wim->tx_sw_internode_queue_size = ATH11K_WIFIILI_MAX_TX_PROCESSQ; ++ ++ dim = &wim->wtdim; ++ dam = &wim->wtdam; ++ ++ dim->num_pool = ab->num_radios; ++ dim->num_tx_device_limit = ATH11K_WIFILI_MAX_TX_DESC; ++ ++ //TODO Revisit below calc based on platform/mem cfg ++ switch (dim->num_pool) { ++ case 1: ++ tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC; ++ break; ++ case 2: ++ tx_desc_limit_0 = ATH11K_WIFILI_DBDC_NUM_TX_DESC; ++ tx_desc_limit_1 = ATH11K_WIFILI_DBDC_NUM_TX_DESC; ++ break; ++ case 3: ++ tx_desc_limit_0 = ATH11K_WIFILI_DBTC_NUM_TX_DESC; ++ tx_desc_limit_1 = ATH11K_WIFILI_DBTC_NUM_TX_DESC; ++ tx_desc_limit_2 = ATH11K_WIFILI_DBTC_NUM_TX_DESC; ++ break; ++ default: ++ ath11k_warn(ab, "unexpected num radios during tx desc alloc\n"); ++ return -EINVAL; ++ } ++ ++ dim->num_tx_desc = tx_desc_limit_0; ++ dim->num_tx_desc_ext = tx_desc_limit_0; ++ dim->num_tx_desc_2 = tx_desc_limit_1; ++ dim->num_tx_desc_ext_2 = tx_desc_limit_1; ++ dim->num_tx_desc_3 = tx_desc_limit_2; ++ dim->num_tx_desc_ext_3 = tx_desc_limit_2; ++ ++ required_size = (dim->num_tx_desc + dim->num_tx_desc_2 + ++ dim->num_tx_desc_3 + ++ dim->num_pool) * WIFILI_NSS_TX_DESC_SIZE; ++ ++ required_size_ext = (dim->num_tx_desc_ext + dim->num_tx_desc_ext_2 + ++ dim->num_tx_desc_ext_3 + ++ dim->num_pool) * WIFILI_NSS_TX_EXT_DESC_SIZE; ++ ++ if (ath11k_nss_tx_desc_mem_alloc(ab, required_size, &page_idx)) { ++ ath11k_warn(ab, "memory allocation for tx desc of size %d failed\n", ++ required_size); ++ return -ENOMEM; ++ } ++ ++ /* Fill the page number from where extension tx descriptor is available */ ++ dim->ext_desc_page_num = page_idx; ++ ++ if (ath11k_nss_tx_desc_mem_alloc(ab, required_size_ext, &page_idx)) { ++ ath11k_warn(ab, "memory allocation for extension tx desc of size %d failed\n", ++ required_size_ext); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < page_idx; i++) { ++ if (i < NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG) { ++ dim->memory_addr[i] = (u32)ab->nss.tx_desc_paddr[i]; ++ dim->memory_size[i] = (u32)ab->nss.tx_desc_size[i]; ++ dim->num_memaddr++; ++ } else { ++ dam_page_idx = i - NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG; ++ dam->addtnl_memory_addr[dam_page_idx] = (u32)ab->nss.tx_desc_paddr[i]; ++ dam->addtnl_memory_size[dam_page_idx] = (u32)ab->nss.tx_desc_size[i]; ++ dam->num_addtnl_addr++; ++ } ++ } ++ ++ if (i > NSS_WIFILI_MAX_NUMBER_OF_PAGE_MSG) ++ wim->flags |= WIFILI_ADDTL_MEM_SEG_SET; ++ ++ return 0; ++} ++ ++static int ath11k_nss_get_target_type(struct ath11k_base *ab) ++{ ++ switch (ab->hw_rev) { ++ case ATH11K_HW_IPQ8074: ++ return ATH11K_WIFILI_TARGET_TYPE_QCA8074V2; ++ case ATH11K_HW_IPQ6018_HW10: ++ return ATH11K_WIFILI_TARGET_TYPE_QCA6018; ++ case ATH11K_HW_QCN9074_HW10: ++ return ATH11K_WIFILI_TARGET_TYPE_QCN9074; ++ case ATH11K_HW_IPQ5018_HW10: ++ return ATH11K_WIFILI_TARGET_TYPE_QCA5018; ++ default: ++ ath11k_warn(ab, "NSS Offload not supported for this HW\n"); ++ return ATH11K_WIFILI_TARGET_TYPE_UNKNOWN; ++ } ++} ++ ++static int ath11k_nss_get_interface_type(struct ath11k_base *ab) ++{ ++ switch (ab->hw_rev) { ++ case ATH11K_HW_IPQ8074: ++ case ATH11K_HW_IPQ6018_HW10: ++ case ATH11K_HW_IPQ5018_HW10: ++ return NSS_WIFILI_INTERNAL_INTERFACE; ++ case ATH11K_HW_QCN9074_HW10: ++ return nss_get_available_wifili_external_if(); ++ default: ++ /* This can't happen since we validated target type earlier */ ++ WARN_ON(1); ++ return NSS_MAX_NET_INTERFACES; ++ } ++} ++ ++static int ath11k_nss_get_dynamic_interface_type(struct ath11k_base *ab) ++{ ++ switch (ab->nss.if_num) { ++ case NSS_WIFILI_INTERNAL_INTERFACE: ++ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_INTERNAL; ++ case NSS_WIFILI_EXTERNAL_INTERFACE0: ++ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL0; ++ case NSS_WIFILI_EXTERNAL_INTERFACE1: ++ return NSS_DYNAMIC_INTERFACE_TYPE_WIFILI_EXTERNAL1; ++ default: ++ ath11k_warn(ab, "NSS Offload invalid interface\n"); ++ return NSS_DYNAMIC_INTERFACE_TYPE_NONE; ++ } ++} ++ ++static int ath11k_nss_init(struct ath11k_base *ab) ++{ ++ struct nss_wifili_init_msg *wim = NULL; ++ struct nss_wifili_msg *wlmsg = NULL; ++ struct nss_ctx_instance *nss_contex; ++ nss_wifili_msg_callback_t msg_cb; ++ u32 target_type; ++ u32 features = 0; ++ nss_tx_status_t status; ++ struct ath11k_dp *dp; ++ int i, ret; ++ ++ dp = &ab->dp; ++ ++ target_type = ath11k_nss_get_target_type(ab); ++ ++ if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN) ++ return -ENOTSUPP; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ wim = &wlmsg->msg.init; ++ ++ wim->target_type = target_type; ++ ++ /* fill rx parameters to initialize rx context */ ++ wim->wrip.tlv_size = ab->hw_params.hal_desc_sz; ++ wim->wrip.rx_buf_len = DP_RX_BUFFER_SIZE; ++ ++ /* fill hal srng message */ ++ wim->hssm.dev_base_addr = (u32)ab->mem_pa; ++ wim->hssm.shadow_rdptr_mem_addr = (u32)ab->hal.rdp.paddr; ++ wim->hssm.shadow_wrptr_mem_addr = (u32)ab->hal.wrp.paddr; ++ wim->hssm.lmac_rings_start_id = HAL_SRNG_RING_ID_LMAC1_ID_START; ++ ++ /* fill TCL data/completion ring info */ ++ wim->num_tcl_data_rings = DP_TCL_NUM_RING_MAX; ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_data_ring.ring_id, ++ &wim->tcl_ring_info[i]); ++ ath11k_nss_fill_srng_info(ab, dp->tx_ring[i].tcl_comp_ring.ring_id, ++ &wim->tx_comp_ring[i]); ++ } ++ ++ /* allocate tx desc memory for NSS and fill corresponding info */ ++ ret = ath11k_nss_fill_tx_desc_info(ab, wim); ++ if (ret) ++ goto free; ++ ++ /* fill reo dest ring info */ ++ wim->num_reo_dest_rings = DP_REO_DST_RING_MAX; ++ for (i = 0; i < DP_REO_DST_RING_MAX; i++) { ++ ath11k_nss_fill_srng_info(ab, dp->reo_dst_ring[i].ring_id, ++ &wim->reo_dest_ring[i]); ++ } ++ ++ /* fill reo reinject ring info */ ++ ath11k_nss_fill_srng_info(ab, dp->reo_reinject_ring.ring_id, ++ &wim->reo_reinject_ring); ++ ++ /* fill reo release ring info */ ++ ath11k_nss_fill_srng_info(ab, dp->rx_rel_ring.ring_id, ++ &wim->rx_rel_ring); ++ ++ /* fill reo exception ring info */ ++ ath11k_nss_fill_srng_info(ab, dp->reo_except_ring.ring_id, ++ &wim->reo_exception_ring); ++ ++ ab->nss.if_num = ath11k_nss_get_interface_type(ab); ++ ++ ath11k_info(ab, "nss init soc nss if_num %d userpd_id %d\n", ab->nss.if_num, ab->userpd_id); ++ ++ if (ab->nss.if_num >= NSS_MAX_NET_INTERFACES) { ++ ath11k_warn(ab, "NSS invalid interface\n"); ++ goto free; ++ } ++ ++ /* register callbacks for events and exceptions with nss */ ++ nss_contex = nss_register_wifili_if(ab->nss.if_num, NULL, ++ (nss_wifili_callback_t)ath11k_nss_wifili_ext_callback_fn, ++ (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive, ++ (struct net_device *)ab, features); ++ ++ if (!nss_contex) { ++ ath11k_warn(ab, "nss wifili register failure\n"); ++ goto free; ++ } ++ ++ if (nss_cmn_get_state(nss_contex) != NSS_STATE_INITIALIZED) { ++ ath11k_warn(ab, "nss state in default init state\n"); ++ goto free; ++ } ++ ++ /* The registered soc context is stored in ab, and will be used for ++ * all soc related messages with nss ++ */ ++ ab->nss.ctx = nss_contex; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ /* Initialize the common part of the wlmsg */ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_INIT_MSG, ++ sizeof(struct nss_wifili_init_msg), ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during init sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(nss_contex, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failure to send nss init msg\n"); ++ goto unregister; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for nss init msg response\n"); ++ goto unregister; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) ++ goto unregister; ++ ++ kfree(wlmsg); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Init Message TX Success %p %d\n", ++ ab->nss.ctx, ab->nss.if_num); ++ return 0; ++ ++unregister: ++ nss_unregister_wifili_if(ab->nss.if_num); ++free: ++ ath11k_nss_tx_desc_mem_free(ab); ++ kfree(wlmsg); ++ return -EINVAL; ++} ++ ++static int ath11k_nss_stats_cfg(struct ath11k *ar, int nss_msg, int enable) ++{ ++ struct nss_wifili_msg *wlmsg = NULL; ++ struct nss_wifili_stats_cfg_msg *stats_cfg; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ struct ath11k_base *ab = ar->ab; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ stats_cfg = &wlmsg->msg.scm; ++ stats_cfg->cfg = enable; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ nss_msg, ++ sizeof(struct nss_wifili_stats_cfg_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss stats cfg %d msg tx failure\n", nss_msg); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss stats %d enable %d\n", nss_msg, enable); ++ ++free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++static void ath11k_nss_sojourn_stats_disable(struct ath11k *ar) ++{ ++ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_V2_CFG_MSG, ++ ATH11K_NSS_STATS_DISABLE); ++} ++ ++void ath11k_nss_peer_stats_disable(struct ath11k *ar) ++{ ++ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG, ++ ATH11K_NSS_STATS_DISABLE); ++} ++ ++void ath11k_nss_peer_stats_enable(struct ath11k *ar) ++{ ++ ath11k_nss_stats_cfg(ar, NSS_WIFILI_STATS_CFG_MSG, ++ ATH11K_NSS_STATS_ENABLE); ++} ++ ++int ath11k_nss_pdev_init(struct ath11k_base *ab, int radio_id) ++{ ++ struct ath11k *ar = ab->pdevs[radio_id].ar; ++ struct nss_wifili_pdev_init_msg *pdevmsg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int radio_if_num = -1; ++ int refill_ring_id; ++ int features = 0; ++ int dyn_if_type; ++ int ret, i; ++ ++ dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab); ++ ++ /* Allocate a node for dynamic interface */ ++ radio_if_num = nss_dynamic_interface_alloc_node(dyn_if_type); ++ ++ if (radio_if_num < 0) ++ return -EINVAL; ++ ++ /* The ifnum and registered radio context is stored in ar and used ++ * for messages related to vdev/radio ++ */ ++ ar->nss.if_num = radio_if_num; ++ ++ /* No callbacks are registered for radio specific events/data */ ++ ar->nss.ctx = nss_register_wifili_radio_if((u32)radio_if_num, NULL, ++ NULL, NULL, (struct net_device *)ar, ++ features); ++ ++ if (!ar->nss.ctx) { ++ ath11k_warn(ab, "failure during nss pdev register\n"); ++ ret = -EINVAL; ++ goto dealloc; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss pdev init - id:%d init ctxt:%p ifnum:%d\n", ++ ar->pdev->pdev_id, ar->nss.ctx, ar->nss.if_num); ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) { ++ ret = -ENOMEM; ++ goto unregister; ++ } ++ ++ pdevmsg = &wlmsg->msg.pdevmsg; ++ ++ pdevmsg->radio_id = radio_id; ++ pdevmsg->lmac_id = ar->lmac_id; ++ pdevmsg->target_pdev_id = ar->pdev->pdev_id; ++ pdevmsg->num_rx_swdesc = WIFILI_RX_DESC_POOL_WEIGHT * DP_RXDMA_BUF_RING_SIZE; ++ ++ /* Store rxdma ring info to the message */ ++ refill_ring_id = ar->dp.rx_refill_buf_ring.refill_buf_ring.ring_id; ++ ath11k_nss_fill_srng_info(ab, refill_ring_id, &pdevmsg->rxdma_ring); ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_PDEV_INIT_MSG, ++ sizeof(struct nss_wifili_pdev_init_msg), ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during init sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send pdev msg tx error : %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for pdev init msg response\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ kfree(wlmsg); ++ ++ /* Disable nss sojourn stats by default */ ++ ath11k_nss_sojourn_stats_disable(ar); ++ /* Enable nss wifili peer stats by default */ ++ ath11k_nss_peer_stats_enable(ar); ++ ++ /*TODO CFG Tx buffer limit as per no clients range per radio ++ * this needs to be based on target/mem cfg ++ * similar to tx desc cfg at soc init per radio ++ */ ++ ++ for (i = 0; i < ATH11K_NSS_RADIO_TX_LIMIT_RANGE; i++) ++ ath11k_nss_radio_buf_cfg(ar, i, ATH11K_NSS_RADIO_TX_LIMIT_RANGE3); ++ ++ return 0; ++ ++free: ++ kfree(wlmsg); ++unregister: ++ nss_unregister_wifili_radio_if(ar->nss.if_num); ++dealloc: ++ nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type); ++ return ret; ++} ++ ++/* TODO : Check if start, reset and stop messages can be done using single function as ++ * body is similar, having it now for clarity */ ++ ++int ath11k_nss_start(struct ath11k_base *ab) ++{ ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ /* Empty message for NSS Start message */ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_START_MSG, ++ 0, ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during init sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send start msg tx error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for response for nss start msg\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ /* NSS Start success */ ++ ret = 0; ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "nss start success\n"); ++ ++free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++static void ath11k_nss_reset(struct ath11k_base *ab) ++{ ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS reset\n"); ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) { ++ ath11k_warn(ab, "mem allocation failure during nss reset\n"); ++ return; ++ } ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ /* Empty message for NSS Reset message */ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_SOC_RESET_MSG, ++ 0, ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during deinit sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ ++ /* Add a retry mechanism to reset nss until success */ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send reset msg tx error %d\n", status); ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for response for nss reset msg\n"); ++ goto free; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ath11k_warn(ab, "failure response during nss reset %d\n", ab->nss.response); ++ goto free; ++ } ++ ++ /* Unregister wifili interface */ ++ nss_unregister_wifili_if(ab->nss.if_num); ++ ++free: ++ kfree(wlmsg); ++} ++ ++static int ath11k_nss_stop(struct ath11k_base *ab) ++{ ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS stop\n"); ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ /* Empty message for Stop command */ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_STOP_MSG, ++ 0, ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during deinit sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ ++ /* Add a retry mechanism to stop nss until success */ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send stop msg tx error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for response for nss stop msg\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ /* NSS Stop success */ ++ ret = 0; ++free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++int ath11k_nss_pdev_deinit(struct ath11k_base *ab, int radio_id) ++{ ++ struct ath11k *ar = ab->pdevs[radio_id].ar; ++ struct nss_wifili_pdev_deinit_msg *deinit; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int dyn_if_type; ++ int ret; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS pdev %d deinit\n", radio_id); ++ dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab); ++ ++ /* Disable NSS wifili peer stats before teardown */ ++ if (ab->nss.stats_enabled) ++ ath11k_nss_peer_stats_disable(ar); ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ deinit = &wlmsg->msg.pdevdeinit; ++ deinit->ifnum = radio_id; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_PDEV_DEINIT_MSG, ++ sizeof(struct nss_wifili_pdev_deinit_msg), ++ msg_cb, NULL); ++ ++ reinit_completion(&ab->nss.complete); ++ ++ /* Note: response is contention free during deinit sequence */ ++ ab->nss.response = ATH11K_NSS_MSG_ACK; ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss send pdev deinit msg tx error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for pdev deinit msg response\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ /* Check if the response is success from the callback */ ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ /* pdev deinit msg success, dealloc, deregister and return */ ++ ret = 0; ++ ++ nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type); ++ nss_unregister_wifili_radio_if(ar->nss.if_num); ++free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++int ath11k_nss_teardown(struct ath11k_base *ab) ++{ ++ int i, ret; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ ath11k_nss_stop(ab); ++ ++ for (i = 0; i < ab->num_radios ; i++) { ++ ret = ath11k_nss_pdev_deinit(ab, i); ++ if (ret) ++ ath11k_warn(ab, "failure during pdev%d deinit\n", i); ++ } ++ ++ ath11k_nss_reset(ab); ++ ath11k_nss_tx_desc_mem_free(ab); ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Teardown Complete\n"); ++ ++ return 0; ++} ++ ++int ath11k_nss_setup(struct ath11k_base *ab) ++{ ++ int i; ++ int ret = 0; ++ u32 target_type; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ target_type = ath11k_nss_get_target_type(ab); ++ ++ if (target_type == ATH11K_WIFILI_TARGET_TYPE_UNKNOWN) ++ return -ENOTSUPP; ++ ++ /* Verify NSS support is enabled */ ++ if (nss_cmn_get_nss_enabled() == false) { ++ ath11k_warn(ab, "NSS offload support disabled, falling back to default mode\n"); ++ return -ENOTSUPP; ++ } ++ ++ /* Initialize completion for verifying NSS message response */ ++ init_completion(&ab->nss.complete); ++ ++ /* Setup common resources for NSS */ ++ ret = ath11k_nss_init(ab); ++ if (ret) { ++ ath11k_warn(ab, "NSS SOC Initialization Failed :%d\n", ret); ++ goto fail; ++ } ++ ++ /* Setup pdev related resources for NSS */ ++ for (i = 0; i < ab->num_radios; i++) { ++ ret = ath11k_nss_pdev_init(ab, i); ++ if (ret) { ++ ath11k_warn(ab, "NSS PDEV %d Initialization Failed :%d\n", i, ret); ++ goto pdev_deinit; ++ } ++ } ++ ++ /* Set the NSS statemachine to start */ ++ ret = ath11k_nss_start(ab); ++ if (ret) { ++ ath11k_warn(ab, "NSS Start Failed : %d\n", ret); ++ goto pdev_deinit; ++ } ++ ++ /* Default nexthop interface is set to ETH RX */ ++ ret = nss_wifi_vdev_base_set_next_hop(ab->nss.ctx, NSS_ETH_RX_INTERFACE); ++ if (ret != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "Failure to set default next hop : %d\n", ret); ++ goto stop; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Setup Complete\n"); ++ return ret; ++ ++stop: ++ ath11k_nss_stop(ab); ++ ++pdev_deinit: ++ for (i -= 1; i >= 0; i--) ++ ath11k_nss_pdev_deinit(ab, i); ++ ++ ath11k_nss_reset(ab); ++ ath11k_nss_tx_desc_mem_free(ab); ++fail: ++ return ret; ++} +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -0,0 +1,307 @@ ++/* SPDX-License-Identifier: BSD-3-Clause-Clear */ ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef ATH11K_NSS_H ++#define ATH11K_NSS_H ++ ++#include "net/cfg80211.h" ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++#include ++#include ++ ++#endif ++struct ath11k; ++struct ath11k_base; ++struct ath11k_vif; ++struct ath11k_peer; ++struct ath11k_sta; ++struct hal_rx_mon_ppdu_info; ++struct hal_rx_user_status; ++ ++/* NSS DBG macro is not included as part of debug enum to avoid ++ * frequent changes during upgrade*/ ++#define ATH11K_DBG_NSS 0x80000000 ++ ++/* WIFILI Supported Target Types */ ++#define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN 0xFF ++#define ATH11K_WIFILI_TARGET_TYPE_QCA8074 20 ++#define ATH11K_WIFILI_TARGET_TYPE_QCA8074V2 24 ++#define ATH11K_WIFILI_TARGET_TYPE_QCA6018 25 ++#define ATH11K_WIFILI_TARGET_TYPE_QCN9074 26 ++#define ATH11K_WIFILI_TARGET_TYPE_QCA5018 29 ++ ++/* Max limit for NSS Queue */ ++#define ATH11K_WIFIILI_MAX_TX_PROCESSQ 1024 ++ ++/* Max TX Desc limit */ ++#define ATH11K_WIFILI_MAX_TX_DESC 65536 ++ ++/* TX Desc related info */ ++/*TODO : Check this again during experiments for lowmem or ++ changes for platforms based on num radios supported */ ++#define ATH11K_WIFILI_DBDC_NUM_TX_DESC (1024 * 8) ++#define ATH11K_WIFILI_DBTC_NUM_TX_DESC (1024 * 8) ++ ++// TODO Revisit these page size calc ++#define WIFILI_NSS_TX_DESC_SIZE 20*4 ++#define WIFILI_NSS_TX_EXT_DESC_SIZE 40*4 ++/* Number of desc per page(12bit) should be<4096, page limit per 1024 byte is 80*3=240 */ ++#define WIFILI_NSS_TX_DESC_PAGE_LIMIT 240 ++#define WIFILI_NSS_MAX_MEM_PAGE_SIZE (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024) ++#define WIFILI_NSS_MAX_EXT_MEM_PAGE_SIZE (WIFILI_NSS_TX_DESC_PAGE_LIMIT * 1024) ++#define WIFILI_RX_DESC_POOL_WEIGHT 3 ++ ++/* Status of the NSS messages sent from driver */ ++#define ATH11K_NSS_MSG_ACK 0 ++/* Timeout for waiting for response from NSS on TX msg */ ++#define ATH11K_NSS_MSG_TIMEOUT_MS 5000 ++ ++/* Init Flags */ ++#define WIFILI_NSS_CCE_DISABLED 0x1 ++#define WIFILI_ADDTL_MEM_SEG_SET 0x000000002 ++ ++/* ATH11K NSS PEER Info */ ++/* Host memory allocated for peer info storage in nss */ ++#define WIFILI_NSS_PEER_BYTE_SIZE NSS_WIFILI_PEER_SIZE ++ ++/* ATH11K NSS Stats */ ++#define ATH11K_NSS_STATS_ENABLE 1 ++#define ATH11K_NSS_STATS_DISABLE 0 ++ ++/* TX Buf cfg range */ ++#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE 4 ++ ++/* TODO : Analysis based on platform */ ++/* TX Limit till 64 clients */ ++#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE0 8192 ++/* TX Limit till 128 clients */ ++#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE1 8192 ++/* TX Limit till 256 clients */ ++#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE2 8192 ++/* TX Limit > 256 clients */ ++#define ATH11K_NSS_RADIO_TX_LIMIT_RANGE3 8192 ++ ++#define ATH11K_NSS_MAX_NUMBER_OF_PAGE 96 ++ ++#define NSS_TX_TID_MAX 8 ++ ++#define ATH11K_NSS_TXRX_NETDEV_STATS(txrx, vif, len, pkt_count) \ ++do { \ ++ struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); \ ++ struct pcpu_sw_netstats *tstats; \ ++ \ ++ if (!wdev) \ ++ break; \ ++ tstats = this_cpu_ptr(netdev_tstats(wdev->netdev)); \ ++ u64_stats_update_begin(&tstats->syncp); \ ++ u64_stats_add(&tstats->txrx ## _packets, pkt_count); \ ++ u64_stats_add(&tstats->txrx ## _bytes, len); \ ++ u64_stats_update_end(&tstats->syncp); \ ++} while (0) ++ ++enum ath11k_nss_opmode { ++ ATH11K_NSS_OPMODE_UNKNOWN, ++ ATH11K_NSS_OPMODE_AP, ++ ATH11K_NSS_OPMODE_IBSS, ++ ATH11K_NSS_OPMODE_STA, ++ ATH11K_NSS_OPMODE_MONITOR, ++}; ++ ++struct peer_stats { ++ u64 last_rx; ++ u64 last_ack; ++ u32 tx_packets; ++ u32 tx_bytes; ++ u32 tx_retries; ++ u32 tx_failed; ++ u32 rx_packets; ++ u32 rx_bytes; ++ u32 rx_dropped; ++ u32 last_rxdrop; ++ struct rate_info rxrate; ++}; ++ ++enum ath11k_nss_peer_sec_type { ++ PEER_SEC_TYPE_NONE, ++ PEER_SEC_TYPE_WEP128, ++ PEER_SEC_TYPE_WEP104, ++ PEER_SEC_TYPE_WEP40, ++ PEER_SEC_TYPE_TKIP, ++ PEER_SEC_TYPE_TKIP_NOMIC, ++ PEER_SEC_TYPE_AES_CCMP, ++ PEER_SEC_TYPE_WAPI, ++ PEER_SEC_TYPE_AES_CCMP_256, ++ PEER_SEC_TYPE_AES_GCMP, ++ PEER_SEC_TYPE_AES_GCMP_256, ++ PEER_SEC_TYPES_MAX ++}; ++ ++/* this holds the memory allocated for nss managed peer info */ ++struct ath11k_nss_peer { ++ uint32_t *vaddr; ++ dma_addr_t paddr; ++ struct peer_stats *nss_stats; ++ struct completion complete; ++}; ++ ++/* Structure to hold the vif related info for nss offload support */ ++struct arvif_nss { ++ /* dynamic ifnum allocated by nss driver for vif */ ++ int if_num; ++ /* Used for completion status for vdev config nss messages */ ++ struct completion complete; ++ /* Keep the copy of encap type for nss */ ++ int encap; ++ /* Keep the copy of decap type for nss */ ++ int decap; ++ bool created; ++}; ++ ++/* Structure to hold the pdev/radio related info for nss offload support */ ++struct ath11k_nss { ++ /* dynamic ifnum allocated by nss driver for pdev */ ++ int if_num; ++ /* Radio/pdev Context obtained on pdev register */ ++ void* ctx; ++}; ++ ++/* Structure to hold the soc related info for nss offload support */ ++struct ath11k_soc_nss { ++ /* turn on/off nss offload support in ath11k */ ++ bool enabled; ++ /* turn on/off nss stats support in ath11k */ ++ bool stats_enabled; ++ /* soc nss ctx */ ++ void* ctx; ++ /* if_num to be used for soc related nss messages */ ++ int if_num; ++ /* debug mode to disable the regular mesh configuration from mac80211 */ ++ bool debug_mode; ++ /* Completion to nss message response */ ++ struct completion complete; ++ /* Response to nss messages are stored here on msg callback ++ * used only in contention free messages during init */ ++ int response; ++ /* Below is used for identifying allocated tx descriptors */ ++ dma_addr_t tx_desc_paddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE]; ++ uint32_t * tx_desc_vaddr[ATH11K_NSS_MAX_NUMBER_OF_PAGE]; ++ uint32_t tx_desc_size[ATH11K_NSS_MAX_NUMBER_OF_PAGE]; ++}; ++ ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb); ++int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val); ++int ath11k_nss_vdev_create(struct ath11k_vif *arvif); ++void ath11k_nss_vdev_delete(struct ath11k_vif *arvif); ++int ath11k_nss_vdev_up(struct ath11k_vif *arvif); ++int ath11k_nss_vdev_down(struct ath11k_vif *arvif); ++int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr); ++int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id); ++int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer); ++void ath11k_nss_peer_stats_enable(struct ath11k *ar); ++void ath11k_nss_peer_stats_disable(struct ath11k *ar); ++int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer, ++ struct ieee80211_key_conf *key_conf); ++void ath11k_nss_update_sta_stats(struct station_info *sinfo, ++ struct ieee80211_sta *sta, ++ struct ath11k_sta *arsta); ++void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info, ++ struct ath11k_peer *peer, ++ struct hal_rx_user_status *user_stats); ++int ath11k_nss_setup(struct ath11k_base *ab); ++int ath11k_nss_teardown(struct ath11k_base *ab); ++void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter); ++#else ++static inline int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_vdev_create(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id) ++{ ++ return 0; ++} ++ ++static inline void ath11k_nss_vdev_delete(struct ath11k_vif *arvif) ++{ ++} ++ ++static inline void ath11k_nss_update_sta_stats(struct station_info *sinfo, ++ struct ieee80211_sta *sta, ++ struct ath11k_sta *arsta) ++{ ++ return; ++} ++ ++static inline void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info, ++ struct ath11k_peer *peer, ++ struct hal_rx_user_status *user_stats) ++{ ++ return; ++} ++ ++static inline int ath11k_nss_vdev_up(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_vdev_down(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer) ++{ ++ return 0; ++} ++ ++static inline void ath11k_nss_peer_stats_enable(struct ath11k *ar) ++{ ++ return; ++} ++ ++static inline void ath11k_nss_peer_stats_disable(struct ath11k *ar) ++{ ++ return; ++} ++ ++static inline int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer, ++ struct ieee80211_key_conf *key_conf) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_setup(struct ath11k_base *ab) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_teardown(struct ath11k_base *ab) ++{ ++ return 0; ++} ++ ++static inline void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, ++ struct htt_rx_ring_tlv_filter *tlv_filter) ++{ ++ return; ++} ++#endif /* CPTCFG_ATH11K_NSS_SUPPORT */ ++#endif +--- a/drivers/net/wireless/ath/ath11k/hif.h ++++ b/drivers/net/wireless/ath/ath11k/hif.h +@@ -30,6 +30,7 @@ struct ath11k_hif_ops { + void (*ce_irq_enable)(struct ath11k_base *ab); + void (*ce_irq_disable)(struct ath11k_base *ab); + void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); ++ u32 (*get_window_offset)(struct ath11k_base *ab, u32 offset); + }; + + static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) +@@ -136,6 +137,14 @@ static inline void ath11k_get_msi_addres + ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi); + } + ++static inline u32 ath11k_hif_get_window_offset(struct ath11k_base *ab, u32 offset) ++{ ++ if (ab->hif.ops->get_window_offset) ++ return ab->hif.ops->get_window_offset(ab, offset); ++ ++ return offset; ++} ++ + static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, + u32 *msi_data_idx) + { +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -67,6 +67,20 @@ static u32 ath11k_pci_get_window_start(s + return ATH11K_PCI_WINDOW_START; + } + ++static inline u32 ath11k_pci_get_window_offset(struct ath11k_base *ab, ++ u32 offset) ++{ ++ u32 window_start; ++ ++ if (ab->hw_params.static_window_map) { ++ window_start = ath11k_pci_get_window_start(ab, offset); ++ ++ if (window_start) ++ offset = window_start + (offset & ATH11K_PCI_WINDOW_RANGE_MASK); ++ } ++ return offset; ++} ++ + static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset) + { + struct ath11k_base *ab = ab_pci->ab; +@@ -710,6 +724,7 @@ static const struct ath11k_hif_ops ath11 + .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, + .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, + .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, ++ .get_window_offset = ath11k_pci_get_window_offset, + .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, + }; + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -992,6 +992,29 @@ unlock_exit: + spin_unlock_bh(&ab->base_lock); + } + ++/* Sends WMI config to filter packets to route packets to WBM release ring */ ++int ath11k_dp_rx_pkt_type_filter(struct ath11k *ar, enum ath11k_routing_pkt_type pkt_type, u32 meta_data) ++{ ++ struct ath11k_wmi_pkt_route_param param; ++ int ret; ++ ++ /* Routing Eapol packets to CCE is only allowed now */ ++ if (pkt_type != ATH11K_PKT_TYPE_EAP) ++ return -EINVAL; ++ ++ param.opcode = ATH11K_WMI_PKTROUTE_ADD; ++ param.meta_data = meta_data; ++ param.dst_ring = ATH11K_ROUTE_WBM_RELEASE; ++ param.dst_ring_handler = ATH11K_WMI_PKTROUTE_USE_CCE; ++ param.route_type_bmap = 1 << pkt_type; ++ ++ ret = ath11k_wmi_send_pdev_pkt_route(ar, ¶m); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to configure pkt route %d", ret); ++ ++ return ret; ++} ++ + int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn, + enum hal_pn_type pn_type) +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1164,6 +1164,44 @@ int ath11k_wmi_send_pdev_set_regdomain(s + return ret; + } + ++int ath11k_wmi_send_pdev_pkt_route(struct ath11k *ar, struct ath11k_wmi_pkt_route_param *param) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_pdev_pkt_route_cmd *cmd; ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_pdev_pkt_route_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, ++ WMI_TAG_PDEV_UPDATE_PKT_ROUTING_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ cmd->pdev_id = ar->pdev->pdev_id; ++ cmd->opcode = param->opcode; ++ cmd->route_type_bmap = param->route_type_bmap; ++ cmd->dst_ring = param->dst_ring; ++ cmd->meta_data = param->meta_data; ++ cmd->dst_ring_handler = param->dst_ring_handler; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "WMI pdev pkt route opcode %d route_bmap %d dst_ring %d meta_datan %d dst_ringg_handler %d\n", ++ param->opcode, param->route_type_bmap, ++ param->dst_ring, param->meta_data, param->dst_ring_handler); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_UPDATE_PKT_ROUTING_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to send WMI_PDEV_UPDATE_PKT_ROUTING cmd\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; ++} ++ + int ath11k_wmi_set_peer_param(struct ath11k *ar, const u8 *peer_addr, + u32 vdev_id, u32 param_id, u32 param_val) + { +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -2873,6 +2873,27 @@ struct pdev_set_regdomain_params { + u32 pdev_id; + }; + ++ /* Defines various options for routing policy */ ++enum wmi_pdev_dest_ring_handler_type { ++ ATH11K_WMI_PKTROUTE_USE_CCE = 0, ++ ATH11K_WMI_PKTROUTE_USE_ASPT = 1, ++ ATH11K_WMI_PKTROUTE_USE_FSE = 2, ++ ATH11K_WMI_PKTROUTE_USE_CCE2 = 3, ++}; ++ ++enum ath11k_wmi_pkt_route_opcode { ++ ATH11K_WMI_PKTROUTE_ADD, ++ ATH11K_WMI_PKTROUTE_DEL, ++}; ++ ++struct ath11k_wmi_pkt_route_param { ++ enum ath11k_wmi_pkt_route_opcode opcode; ++ u32 route_type_bmap; ++ u32 dst_ring_handler; ++ u32 dst_ring; ++ u32 meta_data; ++}; ++ + struct rx_reorder_queue_remove_params { + u8 *peer_macaddr; + u16 vdev_id; +@@ -3121,6 +3142,16 @@ struct wmi_pdev_set_regdomain_cmd { + u32 dfs_domain; + } __packed; + ++struct wmi_pdev_pkt_route_cmd { ++ u32 tlv_header; ++ u32 pdev_id; ++ u32 opcode; ++ u32 route_type_bmap; ++ u32 dst_ring; ++ u32 meta_data; ++ u32 dst_ring_handler; ++} __packed; ++ + struct wmi_peer_set_param_cmd { + u32 tlv_header; + u32 vdev_id; +@@ -6362,6 +6393,8 @@ int ath11k_wmi_send_peer_create_cmd(stru + int ath11k_wmi_vdev_set_param_cmd(struct ath11k *ar, u32 vdev_id, + u32 param_id, u32 param_value); + ++int ath11k_wmi_send_pdev_pkt_route(struct ath11k *ar, ++ struct ath11k_wmi_pkt_route_param *param); + int ath11k_wmi_set_sta_ps_param(struct ath11k *ar, u32 vdev_id, + u32 param, u32 param_value); + int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms); +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -20,6 +20,34 @@ + #define DP_RX_MPDU_ERR_MPDU_LEN BIT(6) + #define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) + ++/* different supported pkt types for routing */ ++enum ath11k_routing_pkt_type { ++ ATH11K_PKT_TYPE_ARP_IPV4, ++ ATH11K_PKT_TYPE_NS_IPV6, ++ ATH11K_PKT_TYPE_IGMP_IPV4, ++ ATH11K_PKT_TYPE_MLD_IPV6, ++ ATH11K_PKT_TYPE_DHCP_IPV4, ++ ATH11K_PKT_TYPE_DHCP_IPV6, ++ ATH11K_PKT_TYPE_DNS_TCP_IPV4, ++ ATH11K_PKT_TYPE_DNS_TCP_IPV6, ++ ATH11K_PKT_TYPE_DNS_UDP_IPV4, ++ ATH11K_PKT_TYPE_DNS_UDP_IPV6, ++ ATH11K_PKT_TYPE_ICMP_IPV4, ++ ATH11K_PKT_TYPE_ICMP_IPV6, ++ ATH11K_PKT_TYPE_TCP_IPV4, ++ ATH11K_PKT_TYPE_TCP_IPV6, ++ ATH11K_PKT_TYPE_UDP_IPV4, ++ ATH11K_PKT_TYPE_UDP_IPV6, ++ ATH11K_PKT_TYPE_IPV4, ++ ATH11K_PKT_TYPE_IPV6, ++ ATH11K_PKT_TYPE_EAP, ++ ATH11K_PKT_TYPE_MAX ++}; ++ ++#define ATH11K_RX_PROTOCOL_TAG_START_OFFSET 128 ++#define ATH11K_ROUTE_WBM_RELEASE 5 ++#define ATH11K_ROUTE_EAP_METADATA (ATH11K_RX_PROTOCOL_TAG_START_OFFSET + ATH11K_PKT_TYPE_EAP) ++ + enum dp_rx_decap_type { + DP_RX_DECAP_TYPE_RAW, + DP_RX_DECAP_TYPE_NATIVE_WIFI, +@@ -75,6 +103,9 @@ void ath11k_peer_rx_tid_delete(struct at + int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, + u8 tid, u32 ba_win_sz, u16 ssn, + enum hal_pn_type pn_type); ++int ath11k_dp_rx_pkt_type_filter(struct ath11k *ar, ++ enum ath11k_routing_pkt_type pkt_type, ++ u32 meta_data); + void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, + struct sk_buff *skb); + int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab); +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6367,6 +6367,16 @@ static int ath11k_mac_op_start(struct ie + goto err; + } + ++ /* nss offload requires eapol packets to be routed to wbm release ring */ ++ if (ab->nss.enabled) { ++ ret = ath11k_dp_rx_pkt_type_filter(ar, ATH11K_PKT_TYPE_EAP, ++ ATH11K_ROUTE_EAP_METADATA); ++ if (ret) { ++ ath11k_err(ar->ab, "failed to configure EAP pkt route: %d\n", ret); ++ goto err; ++ } ++ } ++ + __ath11k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask); + + /* TODO: Do we need to enable ANI? */ +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -849,6 +849,8 @@ struct ath11k_soc_dp_tx_err_stats { + * idr unavailable etc. + */ + atomic_t misc_fail; ++ /* Tx failures due to NSS Tx error status */ ++ atomic_t nss_tx_fail; + }; + + struct ath11k_soc_dp_stats { +@@ -935,6 +937,7 @@ struct ath11k_base { + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; ++ int userpd_id; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct ath11k_targ_cap target_caps; diff --git a/package/kernel/mac80211/patches/nss/ath11k/199-003-ath11k-add-nss-support.patch b/package/kernel/mac80211/patches/nss/ath11k/199-003-ath11k-add-nss-support.patch new file mode 100644 index 00000000000000..e30b5d761d3378 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/199-003-ath11k-add-nss-support.patch @@ -0,0 +1,1210 @@ +From 65c511e3aeb9afb84a3c6c8ac34353af91b880e9 Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Fri, 10 Jul 2020 12:50:21 +0530 +Subject: [PATCH 3/3] ath11k: add nss support + + Add NSS Offload support for ath11k driver. + +Signed-off-by: Sriram R +--- + drivers/net/wireless/ath/ath11k/ahb.c | 18 ++++++-- + drivers/net/wireless/ath/ath11k/core.c | 24 ++++++++++ + drivers/net/wireless/ath/ath11k/core.h | 14 +++++- + drivers/net/wireless/ath/ath11k/dp.c | 21 ++++++--- + drivers/net/wireless/ath/ath11k/dp.h | 1 + + drivers/net/wireless/ath/ath11k/dp_rx.c | 17 +++++-- + drivers/net/wireless/ath/ath11k/dp_rx.h | 6 +++ + drivers/net/wireless/ath/ath11k/hal.h | 2 + + drivers/net/wireless/ath/ath11k/hal_rx.c | 10 +++- + drivers/net/wireless/ath/ath11k/mac.c | 78 +++++++++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/peer.c | 9 +++- + drivers/net/wireless/ath/ath11k/peer.h | 6 ++- + local-symbols | 1 + + 13 files changed, 186 insertions(+), 21 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -525,6 +525,12 @@ static int ath11k_ahb_config_ext_irq(str + int i, j; + int irq; + int ret; ++ bool nss_offload; ++ ++ /* TCL Completion, REO Dest, ERR, Exception and h2rxdma rings are offloaded ++ * to nss when its enabled, hence don't enable these interrupts ++ */ ++ nss_offload = ab->nss.enabled; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; +@@ -537,20 +543,20 @@ static int ath11k_ahb_config_ext_irq(str + ath11k_ahb_ext_grp_napi_poll); + + for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { +- if (ab->hw_params.ring_mask->tx[i] & BIT(j)) { ++ if (!nss_offload && ab->hw_params.ring_mask->tx[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + +- if (ab->hw_params.ring_mask->rx[i] & BIT(j)) { ++ if (!nss_offload && ab->hw_params.ring_mask->rx[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + +- if (ab->hw_params.ring_mask->rx_err[i] & BIT(j)) ++ if (!nss_offload && ab->hw_params.ring_mask->rx_err[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + +- if (ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j)) ++ if (!nss_offload && ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ab->hw_params.ring_mask->reo_status[i] & BIT(j)) +@@ -563,7 +569,7 @@ static int ath11k_ahb_config_ext_irq(str + ath11k_hw_get_mac_from_pdev_id(hw, j); + } + +- if (ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) { ++ if (!nss_offload && ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + host2rxdma_host_buf_ring_mac1 - + ath11k_hw_get_mac_from_pdev_id(hw, j); +@@ -904,6 +910,7 @@ static int ath11k_ahb_setup_resources(st + } + + ab->mem = mem; ++ ab->mem_pa = mem_res->start; + ab->mem_len = resource_size(mem_res); + + return 0; +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -17,6 +17,12 @@ + #include "hif.h" + #include "wow.h" + ++unsigned int nss_offload; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++module_param_named(nss_offload, nss_offload, uint, 0644); ++MODULE_PARM_DESC(nss_offload, "Enable NSS Offload support"); ++#endif ++ + unsigned int ath11k_debug_mask; + EXPORT_SYMBOL(ath11k_debug_mask); + module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); +@@ -1528,10 +1534,16 @@ static int ath11k_core_pdev_create(struc + goto err_pdev_debug; + } + ++ ret = ath11k_nss_setup(ab); ++ if (ret) { ++ ath11k_err(ab, "failed to setup nss driver interface%d", ret); ++ goto err_dp_pdev_free; ++ } ++ + ret = ath11k_mac_register(ab); + if (ret) { + ath11k_err(ab, "failed register the radio with mac80211: %d\n", ret); +- goto err_dp_pdev_free; ++ goto err_nss_tear; + } + + ret = ath11k_thermal_register(ab); +@@ -1553,6 +1565,8 @@ err_thermal_unregister: + ath11k_thermal_unregister(ab); + err_mac_unregister: + ath11k_mac_unregister(ab); ++err_nss_tear: ++ ath11k_nss_teardown(ab); + err_dp_pdev_free: + ath11k_dp_pdev_free(ab); + err_pdev_debug: +@@ -1566,6 +1580,10 @@ static void ath11k_core_pdev_destroy(str + ath11k_spectral_deinit(ab); + ath11k_thermal_unregister(ab); + ath11k_mac_unregister(ab); ++ ++ ath11k_nss_teardown(ab); ++ ab->nss.enabled = false; ++ + ath11k_hif_irq_disable(ab); + ath11k_dp_pdev_free(ab); + ath11k_debugfs_pdev_destroy(ab); +@@ -1772,6 +1790,10 @@ static int ath11k_core_reconfigure_on_cr + int ret; + + mutex_lock(&ab->core_lock); ++ ++ ath11k_nss_teardown(ab); ++ ab->nss.enabled = false; ++ + ath11k_thermal_unregister(ab); + ath11k_hif_irq_disable(ab); + ath11k_dp_pdev_free(ab); +@@ -2095,6 +2117,10 @@ int ath11k_core_pre_init(struct ath11k_b + ath11k_err(ab, "failed to get hw params: %d\n", ret); + return ret; + } ++ ab->nss.enabled = nss_offload; ++ ++ if (nss_offload) ++ ab->nss.stats_enabled = 1; + + return 0; + } +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -30,6 +30,7 @@ + #include "spectral.h" + #include "wow.h" + #include "rx_desc.h" ++#include "nss.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -384,6 +385,9 @@ struct ath11k_vif { + #endif /* CPTCFG_ATH11K_DEBUGFS */ + + struct ath11k_mgmt_frame_stats mgmt_stats; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct arvif_nss nss; ++#endif + }; + + struct ath11k_vif_iter { +@@ -537,6 +541,9 @@ struct ath11k_sta { + #endif + + bool use_4addr_set; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct ath11k_nss_sta_stats *nss_stats; ++#endif + u16 tcl_metadata; + + /* Protected with ar->data_lock */ +@@ -632,6 +639,9 @@ struct ath11k { + struct ieee80211_hw *hw; + struct ieee80211_ops *ops; + struct ath11k_pdev_wmi *wmi; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct ath11k_nss nss; ++#endif + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; + struct ath11k_he ar_he; +@@ -892,9 +902,11 @@ struct ath11k_base { + struct ath11k_htc htc; + + struct ath11k_dp dp; ++ struct ath11k_soc_nss nss; + + void __iomem *mem; + void __iomem *mem_ce; ++ dma_addr_t mem_pa; + unsigned long mem_len; + + struct { +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -47,12 +47,17 @@ int ath11k_dp_peer_setup(struct ath11k * + struct ath11k_peer *peer; + u32 reo_dest; + int ret = 0, tid; ++ bool rx_hash_enable = DP_RX_HASH_ENABLE; ++ ++ /* RX Hash based steering is disabled for NSS Offload */ ++ if (ar->ab->nss.enabled) ++ rx_hash_enable = DP_RX_HASH_DISABLE; + + /* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */ + reo_dest = ar->dp.mac_id + 1; + ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id, + WMI_PEER_SET_DEFAULT_ROUTING, +- DP_RX_HASH_ENABLE | (reo_dest << 1)); ++ rx_hash_enable | (reo_dest << 1)); + + if (ret) { + ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n", +@@ -132,6 +137,18 @@ static int ath11k_dp_srng_calculate_msi_ + { + const u8 *grp_mask; + ++ if (ab->nss.enabled) { ++ switch (type) { ++ case HAL_REO_STATUS: ++ case HAL_RXDMA_MONITOR_STATUS: ++ case HAL_RXDMA_MONITOR_DST: ++ case HAL_RXDMA_MONITOR_BUF: ++ break; ++ default: ++ return -ENOENT; ++ } ++ } ++ + switch (type) { + case HAL_WBM2SW_RELEASE: + if (ring_num == DP_RX_RELEASE_RING_NUM) { +@@ -778,14 +795,16 @@ int ath11k_dp_service_srng(struct ath11k + int work_done = 0; + int i, j; + int tot_work_done = 0; ++ bool nss_offload; + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { +- if (BIT(ab->hw_params.hal_params->tcl2wbm_rbm_map[i].wbm_ring_num) & +- ab->hw_params.ring_mask->tx[grp_id]) +- ath11k_dp_tx_completion_handler(ab, i); ++ nss_offload = ab->nss.enabled; ++ ++ if (!nss_offload && ab->hw_params.ring_mask->tx[grp_id]) { ++ i = __fls(ab->hw_params.ring_mask->tx[grp_id]); ++ ath11k_dp_tx_completion_handler(ab, i); + } + +- if (ab->hw_params.ring_mask->rx_err[grp_id]) { ++ if (!nss_offload && ab->hw_params.ring_mask->rx_err[grp_id]) { + work_done = ath11k_dp_process_rx_err(ab, napi, budget); + budget -= work_done; + tot_work_done += work_done; +@@ -793,7 +812,7 @@ int ath11k_dp_service_srng(struct ath11k + goto done; + } + +- if (ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) { ++ if (!nss_offload && ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) { + work_done = ath11k_dp_rx_process_wbm_err(ab, + napi, + budget); +@@ -804,7 +823,7 @@ int ath11k_dp_service_srng(struct ath11k + goto done; + } + +- if (ab->hw_params.ring_mask->rx[grp_id]) { ++ if (!nss_offload && ab->hw_params.ring_mask->rx[grp_id]) { + i = fls(ab->hw_params.ring_mask->rx[grp_id]) - 1; + work_done = ath11k_dp_process_rx(ab, i, napi, + budget); +@@ -838,7 +857,7 @@ int ath11k_dp_service_srng(struct ath11k + if (ab->hw_params.ring_mask->reo_status[grp_id]) + ath11k_dp_process_reo_status(ab); + +- for (i = 0; i < ab->num_radios; i++) { ++ for (i = 0; !nss_offload && i < ab->num_radios; i++) { + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + int id = i * ab->hw_params.num_rxmda_per_pdev + j; + +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -194,6 +194,7 @@ struct ath11k_pdev_dp { + #define DP_AVG_MSDUS_PER_MPDU 4 + + #define DP_RX_HASH_ENABLE 1 /* Enable hash based Rx steering */ ++#define DP_RX_HASH_DISABLE 0 /* Disable hash based Rx steering */ + + #define DP_BA_WIN_SZ_MAX 256 + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -17,6 +17,7 @@ + #include "hal_rx.h" + #include "dp_tx.h" + #include "peer.h" ++#include "nss.h" + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + +@@ -213,8 +214,8 @@ static inline u8 ath11k_dp_rx_h_mpdu_sta + return ab->hw_params.hw_ops->rx_desc_get_mpdu_tid(desc); + } + +-static inline u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_mpdu_peer_id(desc); + } +@@ -225,8 +226,8 @@ static inline u8 ath11k_dp_rx_h_msdu_end + return ab->hw_params.hw_ops->rx_desc_get_l3_pad_bytes(desc); + } + +-static inline bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab, +- struct hal_rx_desc *desc) ++bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) + { + return ab->hw_params.hw_ops->rx_desc_get_first_msdu(desc); + } +@@ -283,7 +284,7 @@ static inline void ath11k_dp_rxdesc_set_ + ab->hw_params.hw_ops->rx_desc_set_msdu_len(desc, len); + } + +-static bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab, ++bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab, + struct hal_rx_desc *desc) + { + struct rx_attention *attn = ath11k_dp_rx_get_attention(ab, desc); +@@ -498,7 +499,9 @@ static int ath11k_dp_rxdma_pdev_buf_setu + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + int i; + +- ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); ++ /* RXDMA BUF ring is offloaded to NSS */ ++ if (!ar->ab->nss.enabled) ++ ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); + + if (ar->ab->hw_params.rxdma1_enable) { + rx_ring = &dp->rxdma_mon_buf_ring; +@@ -2035,7 +2038,7 @@ static void ath11k_dp_rx_h_csum_offload( + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; + } + +-static int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, ++int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, + enum hal_encrypt_type enctype) + { + switch (enctype) { +@@ -2062,7 +2065,7 @@ static int ath11k_dp_rx_crypto_mic_len(s + return 0; + } + +-static int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, ++int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, + enum hal_encrypt_type enctype) + { + switch (enctype) { +@@ -2090,7 +2093,7 @@ static int ath11k_dp_rx_crypto_param_len + return 0; + } + +-static int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, ++int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, + enum hal_encrypt_type enctype) + { + switch (enctype) { +@@ -2826,6 +2829,22 @@ static void ath11k_dp_rx_process_receive + } + } + ++void ath11k_dp_rx_from_nss(struct ath11k *ar, struct sk_buff *msdu, ++ struct napi_struct *napi) ++{ ++ struct ieee80211_rx_status rx_status = {0}; ++ struct ath11k_skb_rxcb *rxcb; ++ ++ rxcb = ATH11K_SKB_RXCB(msdu); ++ ++ ath11k_dp_rx_h_ppdu(ar, rxcb->rx_desc, &rx_status); ++ ath11k_dp_rx_h_mpdu(ar, msdu, rxcb->rx_desc, &rx_status); ++ ++ rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; ++ ++ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); ++} ++ + int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, + struct napi_struct *napi, int budget) + { +@@ -3133,6 +3152,13 @@ static void ath11k_dp_rx_update_user_sta + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + rx_stats = arsta->rx_stats; + ++ if (ar->ab->nss.enabled) ++ ath11k_nss_update_sta_rxrate(ppdu_info, peer, user_stats); ++ ++ /* we've updated rate stats dont update dp rx stats if not enabled */ ++ if (!ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ return; ++ + if (!rx_stats) + return; + +@@ -3209,8 +3235,10 @@ static void ath11k_dp_rx_update_peer_mu_ + { + u32 num_users, i; + +- if (!ath11k_debugfs_is_extd_rx_stats_enabled(ar)) ++ if (!ar->ab->nss.enabled && ++ !ath11k_debugfs_is_extd_rx_stats_enabled(ar)) { + return; ++ } + + num_users = ppdu_info->num_users; + if (num_users > HAL_MAX_UL_MU_USERS) +@@ -5613,7 +5641,7 @@ int ath11k_dp_rx_process_mon_status(stru + struct sk_buff *skb; + struct sk_buff_head skb_list; + struct ath11k_peer *peer; +- struct ath11k_sta *arsta; ++ struct ath11k_sta *arsta = NULL; + int num_buffs_reaped = 0; + u32 rx_buf_sz; + u16 log_type; +@@ -5681,6 +5709,7 @@ int ath11k_dp_rx_process_mon_status(stru + if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { + arsta = (struct ath11k_sta *)peer->sta->drv_priv; + ath11k_dp_rx_update_peer_su_stats(arsta, ppdu_info); ++ ath11k_nss_update_sta_rxrate(ppdu_info, peer, NULL); + } else { + ath11k_dp_rx_mon_process_ulofdma(ppdu_info); + ath11k_dp_rx_update_peer_mu_stats(ar, ppdu_info); +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -145,4 +145,18 @@ int ath11k_peer_rx_frag_setup(struct ath + int ath11k_dp_rx_pktlog_start(struct ath11k_base *ab); + int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer); + ++int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, ++ enum hal_encrypt_type enctype); ++int ath11k_dp_rx_crypto_param_len(struct ath11k *ar, ++ enum hal_encrypt_type enctype); ++int ath11k_dp_rx_crypto_icv_len(struct ath11k *ar, ++ enum hal_encrypt_type enctype); ++bool ath11k_dp_rx_h_msdu_end_first_msdu(struct ath11k_base *ab, ++ struct hal_rx_desc *desc); ++bool ath11k_dp_rx_h_attn_is_mcbc(struct ath11k_base *ab, ++ struct hal_rx_desc *desc); ++u16 ath11k_dp_rx_h_mpdu_start_peer_id(struct ath11k_base *ab, ++ struct hal_rx_desc *desc); ++void ath11k_dp_rx_from_nss(struct ath11k *ar, struct sk_buff *msdu, ++ struct napi_struct *napi); + #endif /* ATH11K_DP_RX_H */ +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -424,6 +424,8 @@ enum hal_srng_ring_id { + #define HAL_SRNG_RING_ID_MAX (HAL_SRNG_RING_ID_UMAC_ID_END + \ + HAL_SRNG_NUM_LMAC_RINGS) + ++#define HAL_SRNG_REO_ALTERNATE_SELECT 0x7 ++ + enum hal_ring_type { + HAL_REO_DST, + HAL_REO_EXCEPTION, +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -938,6 +938,12 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + ppdu_info->num_mpdu_fcs_err = + FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR, + info0); ++ ++ if (ppdu_info->fc_valid) ++ ppdu_info->frame_control = ++ FIELD_GET(HAL_RX_PPDU_END_USER_STATS_INFO2_FRAME_CTRL, ++ __le32_to_cpu(eu_stats->info2)); ++ + switch (ppdu_info->preamble_type) { + case HAL_RX_PREAMBLE_11N: + ppdu_info->ht_flags = 1; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -24,6 +24,7 @@ + #include "debugfs_sta.h" + #include "hif.h" + #include "wow.h" ++#include "nss.h" + + #define CHAN2G(_channel, _freq, _flags) { \ + .band = NL80211_BAND_2GHZ, \ +@@ -1603,6 +1604,11 @@ static void ath11k_control_beaconing(str + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (!info->enable_beacon) { ++ ++ ret = ath11k_nss_vdev_down(arvif); ++ if(ret) ++ ath11k_warn(ar->ab, "failure in nss vdev down %d\r\n",ret); ++ + ret = ath11k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath11k_warn(ar->ab, "failed to down vdev_id %i: %d\n", +@@ -1642,6 +1648,12 @@ static void ath11k_control_beaconing(str + + arvif->is_up = true; + ++ ret = ath11k_nss_vdev_up(arvif); ++ if(ret) { ++ ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret); ++ return; ++ } ++ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %d up\n", arvif->vdev_id); + } + +@@ -3075,6 +3087,12 @@ static void ath11k_bss_assoc(struct ieee + "vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, vif->cfg.aid); + ++ ret = ath11k_nss_vdev_up(arvif); ++ if(ret) { ++ ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret); ++ return; ++ } ++ + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, arvif->bssid); +@@ -3117,6 +3135,10 @@ static void ath11k_bss_disassoc(struct i + + lockdep_assert_held(&ar->conf_mutex); + ++ ret = ath11k_nss_vdev_down(arvif); ++ if(ret) ++ ath11k_warn(ar->ab, "failure in nss vdev down %d\r\n",ret); ++ + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %i disassoc bssid %pM\n", + arvif->vdev_id, arvif->bssid); + +@@ -3397,6 +3419,28 @@ static int ath11k_mac_config_obss_pd(str + return 0; + } + ++static void ath11k_mac_op_nss_bss_info_changed(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u32 changed) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ int ret = 0; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "Setting ap_isolate %d to NSS\n", ++ arvif->vif->bss_conf.nss_ap_isolate); ++ if (changed & BSS_CHANGED_NSS_AP_ISOLATE) { ++ ret = ath11k_nss_vdev_set_cmd(arvif, ATH11K_NSS_WIFI_VDEV_CFG_AP_BRIDGE_CMD, ++ !arvif->vif->bss_conf.nss_ap_isolate); ++ if(ret) ++ ath11k_warn(ar->ab, "failed to set ap_isolate in nss %d\n", ret); ++ } ++ ++ mutex_unlock(&ar->conf_mutex); ++} ++ + static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, +@@ -4295,6 +4339,26 @@ static int ath11k_mac_op_set_key(struct + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); ++ ++ /* TODO: Check if vdev specific security cfg is mandatory */ ++ ret = ath11k_nss_vdev_set_cmd(arvif, ATH11K_NSS_WIFI_VDEV_SECURITY_TYPE_CMD, key->cipher); ++ if (ret) { ++ ath11k_warn(ab, "failure to set vdev security type in nss"); ++ goto unlock; ++ } ++ ++ ret = ath11k_nss_set_peer_sec_type(ar, peer, key); ++ if (ret) { ++ ath11k_warn(ab, "failure to set peer security type in nss"); ++ goto unlock; ++ } ++ ++ ret = ath11k_nss_set_peer_authorize(ar, peer->peer_id); ++ if (ret) { ++ ath11k_warn(ab, "failure to authorize peer in nss"); ++ goto unlock; ++ } ++ + if (peer && cmd == SET_KEY) { + peer->keys[key->keyidx] = key; + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { +@@ -4333,9 +4397,8 @@ static int ath11k_mac_op_set_key(struct + break; + } + } +- ++unlock: + spin_unlock_bh(&ab->base_lock); +- + exit: + mutex_unlock(&ar->conf_mutex); + return ret; +@@ -6216,10 +6279,14 @@ static void ath11k_mac_op_tx(struct ieee + if (control->sta) + arsta = ath11k_sta_to_arsta(control->sta); + +- ret = ath11k_dp_tx(ar, arvif, arsta, skb); ++ if (ar->ab->nss.enabled) ++ ret = ath11k_nss_tx(arvif, skb); ++ else ++ ret = ath11k_dp_tx(ar, arvif, arsta, skb); + if (unlikely(ret)) { + ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); ++ return; + } + } + +@@ -6241,6 +6308,8 @@ static int ath11k_mac_config_mon_status_ + + if (enable) { + tlv_filter = ath11k_mac_mon_status_filter_default; ++ ath11k_nss_ext_rx_stats(ar->ab, &tlv_filter); ++ + if (ath11k_debugfs_rx_filter(ar)) + tlv_filter.rx_filter = ath11k_debugfs_rx_filter(ar); + } +@@ -6539,7 +6608,7 @@ static int ath11k_mac_setup_vdev_create_ + return 0; + } + +-static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw, ++static int ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) + { + struct ath11k *ar = hw->priv; +@@ -6585,6 +6654,8 @@ static void ath11k_mac_op_update_vif_off + arvif->vdev_id, ret); + vif->offload_flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; + } ++ ++ return ret; + } + + static bool ath11k_mac_vif_ap_active_any(struct ath11k_base *ab) +@@ -6715,6 +6786,8 @@ static int ath11k_mac_vdev_delete(struct + + reinit_completion(&ar->vdev_delete_done); + ++ ath11k_nss_vdev_delete(arvif); ++ + ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to delete WMI vdev %d: %d\n", +@@ -6855,7 +6928,34 @@ static int ath11k_mac_op_add_interface(s + list_add(&arvif->list, &ar->arvifs); + spin_unlock_bh(&ar->data_lock); + +- ath11k_mac_op_update_vif_offload(hw, vif); ++ ret = ath11k_nss_vdev_create(arvif); ++ if(ret) { ++ ath11k_warn(ab, "failed to create nss vdev %d\n", ret); ++ goto err_vdev_del; ++ } ++ ++ if (ath11k_mac_op_update_vif_offload(hw, vif)) ++ goto err_vdev_del; ++ ++ if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ++ param_value = ATH11K_HW_TXRX_ETHERNET; ++ else if (test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) ++ param_value = ATH11K_HW_TXRX_RAW; ++ else ++ param_value = ATH11K_HW_TXRX_NATIVE_WIFI; ++ ++ ret = ath11k_nss_vdev_set_cmd(arvif, ATH11K_NSS_WIFI_VDEV_ENCAP_TYPE_CMD, param_value); ++ ++ if(ret) { ++ ath11k_warn(ab, "failed to set encap type in nss %d\n", ret); ++ goto err_vdev_del; ++ } ++ ++ ret = ath11k_nss_vdev_set_cmd(arvif, ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD, param_value); ++ if(ret) { ++ ath11k_warn(ab, "failed to set decap type in nss %d\n", ret); ++ goto err_vdev_del; ++ } + + nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1; + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, +@@ -6979,6 +7079,7 @@ err_peer_del: + } + + err_vdev_del: ++ ath11k_nss_vdev_delete(arvif); + ath11k_mac_vdev_delete(ar, arvif); + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); +@@ -7489,6 +7590,10 @@ ath11k_mac_update_vif_chan(struct ath11k + arvif->vdev_id, ret); + continue; + } ++ ++ ret = ath11k_nss_vdev_up(arvif); ++ if(ret) ++ ath11k_warn(ar->ab, "failure in nss vdev up %d\r\n",ret); + } + + /* Restart the internal monitor vdev on new channel */ +@@ -8717,6 +8822,8 @@ static void ath11k_mac_op_sta_statistics + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi) + + ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); ++ ++ ath11k_nss_update_sta_stats(sinfo, sta, arsta); + } + + #if IS_ENABLED(CONFIG_IPV6) +@@ -9144,6 +9251,7 @@ static const struct ieee80211_ops ath11k + .update_vif_offload = ath11k_mac_op_update_vif_offload, + .config = ath11k_mac_op_config, + .bss_info_changed = ath11k_mac_op_bss_info_changed, ++ .nss_bss_info_changed = ath11k_mac_op_nss_bss_info_changed, + .configure_filter = ath11k_mac_op_configure_filter, + .hw_scan = ath11k_mac_op_hw_scan, + .cancel_hw_scan = ath11k_mac_op_cancel_hw_scan, +@@ -9530,7 +9638,8 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(ar->hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(ar->hw, SUPPORTS_AMSDU_IN_AMPDU); +- ieee80211_hw_set(ar->hw, USES_RSS); ++ if(!ab->nss.enabled) ++ ieee80211_hw_set(ar->hw, USES_RSS); + } + + ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; +@@ -9645,6 +9754,9 @@ static int __ath11k_mac_register(struct + ab->hw_params.bios_sar_capa) + ar->hw->wiphy->sar_capa = ab->hw_params.bios_sar_capa; + ++ if (ab->nss.enabled) ++ ieee80211_hw_set(ar->hw, SUPPORTS_NSS_OFFLOAD); ++ + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret); +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -7,6 +7,7 @@ + #include "core.h" + #include "peer.h" + #include "debug.h" ++#include "nss.h" + + static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab, + int peer_id) +@@ -150,6 +151,8 @@ void ath11k_peer_map_event(struct ath11k + ether_addr_copy(peer->addr, mac_addr); + list_add(&peer->list, &ab->peers); + wake_up(&ab->peer_mapping_wq); ++ if (ab->nss.enabled) ++ ath11k_nss_peer_create(ab, peer); + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer map vdev %d peer %pM id %d\n", +@@ -312,17 +315,13 @@ static int __ath11k_peer_delete(struct a + + lockdep_assert_held(&ar->conf_mutex); + ++ reinit_completion(&ar->peer_delete_done); ++ ath11k_nss_peer_delete(ar->ab, addr); ++ + mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_addr(ab, addr); +- /* Check if the found peer is what we want to remove. +- * While the sta is transitioning to another band we may +- * have 2 peer with the same addr assigned to different +- * vdev_id. Make sure we are deleting the correct peer. +- */ +- if (peer && peer->vdev_id == vdev_id) +- ath11k_peer_rhash_delete(ab, peer); + + /* Fallback to peer list search if the correct peer can't be found. + * Skip the deletion of the peer from the rhash since it has already +@@ -341,10 +340,17 @@ static int __ath11k_peer_delete(struct a + return -EINVAL; + } + ++ /* Check if the found peer is what we want to remove. ++ * While the sta is transitioning to another band we may ++ * have 2 peer with the same addr assigned to different ++ * vdev_id. Make sure we are deleting the correct peer. ++ */ ++ if (peer && peer->vdev_id == vdev_id) ++ ath11k_peer_rhash_delete(ab, peer); ++ + spin_unlock_bh(&ab->base_lock); + mutex_unlock(&ab->tbl_mtx_lock); + +- reinit_completion(&ar->peer_delete_done); + + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); + if (ret) { +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -28,6 +28,7 @@ struct ath11k_peer { + u16 ast_hash; + u8 pdev_idx; + u16 hw_peer_id; ++ struct ath11k_nss_peer nss; + + /* protected by ab->data_lock */ + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -570,6 +570,7 @@ static int ath11k_pci_claim(struct ath11 + } + + ab->mem_ce = ab->mem; ++ ab->mem_pa = pci_resource_start(pdev, ATH11K_PCI_BAR_NUM); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "pci_mem 0x%p\n", ab->mem); + return 0; +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -10,11 +10,10 @@ + #include "core.h" + #include "debug.h" + #include "wmi.h" +-#include "hal_rx.h" + #include "dp_tx.h" + #include "debugfs_htt_stats.h" +-#include "peer.h" + #include "hif.h" ++#include "qmi.h" + + struct dentry *debugfs_ath11k; + +@@ -665,6 +664,7 @@ static ssize_t ath11k_write_extd_rx_stat + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath11k_mac_mon_status_filter_default; ++ ath11k_nss_ext_rx_stats(ar->ab, &tlv_filter); + } + + ar->debug.rx_filter = tlv_filter.rx_filter; +@@ -1687,6 +1687,76 @@ static const struct file_operations fops + .open = simple_open + }; + ++ ++static ssize_t ath11k_write_nss_stats(struct file *file, ++ const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k *ar = file->private_data; ++ struct ath11k_base *ab = ar->ab; ++ u8 nss_stats; ++ int ret; ++ ++ if (!ab->nss.enabled) { ++ ath11k_warn(ab, "nss offload not enabled\n"); ++ return -EINVAL; ++ } ++ ++ if (kstrtou8_from_user(ubuf, count, 0, &nss_stats)) ++ return -EINVAL; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (ar->state != ATH11K_STATE_ON) { ++ ret = -ENETDOWN; ++ goto out; ++ } ++ ++ if (nss_stats == ab->nss.stats_enabled) { ++ ret = count; ++ goto out; ++ } ++ ++ if (nss_stats > 0) { ++ ab->nss.stats_enabled = 1; ++ ath11k_nss_peer_stats_enable(ar); ++ } else { ++ ab->nss.stats_enabled = 0; ++ ath11k_nss_peer_stats_disable(ar); ++ } ++ ++ ret = count; ++out: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static ssize_t ath11k_read_nss_stats(struct file *file, ++ char __user *ubuf, ++ size_t count, loff_t *ppos) ++ ++{ ++ char buf[32] = {0}; ++ struct ath11k *ar = file->private_data; ++ struct ath11k_base *ab = ar->ab; ++ int len = 0; ++ ++ mutex_lock(&ar->conf_mutex); ++ len = scnprintf(buf, sizeof(buf) - len, "%08x\n", ++ ab->nss.stats_enabled); ++ mutex_unlock(&ar->conf_mutex); ++ ++ return simple_read_from_buffer(ubuf, count, ppos, buf, len); ++} ++ ++static const struct file_operations fops_nss_stats = { ++ .read = ath11k_read_nss_stats, ++ .write = ath11k_write_nss_stats, ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ + int ath11k_debugfs_register(struct ath11k *ar) + { + struct ath11k_base *ab = ar->ab; +@@ -1753,6 +1823,11 @@ int ath11k_debugfs_register(struct ath11 + &fops_reset_ps_duration); + } + ++ if (ab->nss.enabled) ++ debugfs_create_file("nss_peer_stats_config", 0644, ++ ar->debug.debugfs_pdev, ar, ++ &fops_nss_stats); ++ + return 0; + } + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -12,7 +12,7 @@ + #include "peer.h" + #include "mac.h" + +-static enum hal_tcl_encap_type ++enum hal_tcl_encap_type + ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb) + { + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); +--- a/drivers/net/wireless/ath/ath11k/dp_tx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.h +@@ -38,6 +38,8 @@ int ath11k_dp_tx_htt_rx_filter_setup(str + int mac_id, enum hal_ring_type ring_type, + int rx_buf_size, + struct htt_rx_ring_tlv_filter *tlv_filter); ++enum hal_tcl_encap_type ++ath11k_dp_tx_get_encap_type(struct ath11k_vif *arvif, struct sk_buff *skb); + + int ath11k_dp_tx_htt_rx_full_mon_setup(struct ath11k_base *ab, int mac_id, + bool config); +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -8,6 +8,8 @@ + #include "nss.h" + #include "core.h" + #include "peer.h" ++#include "dp_rx.h" ++#include "dp_tx.h" + #include "hif.h" + #include "wmi.h" + #include "../../../../../net/mac80211/sta_info.h" +@@ -466,7 +468,7 @@ deliver_amsdu: + + /* create list containing all the subframes */ + ieee80211_amsdu_to_8023s(skb, &subframe_list, NULL, +- vif->type, 0, NULL, NULL); ++ vif->type, 0, NULL, NULL, 0); + + /* This shouldn't happen, indicating error during defragmentation */ + if (skb_queue_empty(&subframe_list)) +@@ -658,12 +660,14 @@ drop: + return -EINVAL; + } + +-int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val) ++int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, enum ath11k_nss_vdev_cmd nss_cmd, ++ int val) + { + struct nss_wifi_vdev_msg *vdev_msg = NULL; + struct nss_wifi_vdev_cmd_msg *vdev_cmd; + struct ath11k *ar = arvif->ar; + nss_tx_status_t status; ++ int cmd; + + if (!ar->ab->nss.enabled) + return 0; +@@ -676,6 +680,22 @@ int ath11k_nss_vdev_set_cmd(struct ath11 + if (!vdev_msg) + return -ENOMEM; + ++ switch(nss_cmd) { ++ case ATH11K_NSS_WIFI_VDEV_CFG_AP_BRIDGE_CMD: ++ cmd = NSS_WIFI_VDEV_CFG_AP_BRIDGE_CMD; ++ break; ++ case ATH11K_NSS_WIFI_VDEV_SECURITY_TYPE_CMD: ++ cmd = NSS_WIFI_VDEV_SECURITY_TYPE_CMD; ++ break; ++ case ATH11K_NSS_WIFI_VDEV_ENCAP_TYPE_CMD: ++ cmd = NSS_WIFI_VDEV_ENCAP_TYPE_CMD; ++ break; ++ case ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD: ++ cmd = NSS_WIFI_VDEV_DECAP_TYPE_CMD; ++ break; ++ default: ++ return -EINVAL; ++ } + /* TODO: Convert to function for conversion in case of many + * such commands + */ +@@ -1137,7 +1157,6 @@ void ath11k_nss_update_sta_stats(struct + { + struct sta_info *stainfo; + struct ath11k_peer *peer; +- int tid_idx; + struct ath11k *ar = arsta->arvif->ar; + struct ath11k_base *ab = ar->ab; + +@@ -1231,6 +1250,9 @@ void ath11k_nss_update_sta_rxrate(struct + if (!ab->nss.enabled) + return; + ++ if (!ieee80211_is_data(__cpu_to_le16(ppdu_info->frame_control))) ++ return; ++ + if (!peer->nss.nss_stats) + return; + +@@ -1290,7 +1312,7 @@ void ath11k_nss_update_sta_rxrate(struct + peer->nss.nss_stats->rxrate.mcs = mcs; + peer->nss.nss_stats->rxrate.flags = RATE_INFO_FLAGS_HE_MCS; + peer->nss.nss_stats->rxrate.he_dcm = ppdu_info->dcm; +- peer->nss.nss_stats->rxrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(ppdu_info->gi); ++ peer->nss.nss_stats->rxrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(ppdu_info->gi); + peer->nss.nss_stats->rxrate.he_ru_alloc = ppdu_info->ru_alloc; + break; + } +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -101,6 +101,13 @@ do { \ + u64_stats_update_end(&tstats->syncp); \ + } while (0) + ++enum ath11k_nss_vdev_cmd { ++ ATH11K_NSS_WIFI_VDEV_CFG_AP_BRIDGE_CMD, ++ ATH11K_NSS_WIFI_VDEV_SECURITY_TYPE_CMD, ++ ATH11K_NSS_WIFI_VDEV_ENCAP_TYPE_CMD, ++ ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD, ++}; ++ + enum ath11k_nss_opmode { + ATH11K_NSS_OPMODE_UNKNOWN, + ATH11K_NSS_OPMODE_AP, +@@ -192,7 +199,8 @@ struct ath11k_soc_nss { + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb); +-int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val); ++int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, enum ath11k_nss_vdev_cmd cmd, ++ int val); + int ath11k_nss_vdev_create(struct ath11k_vif *arvif); + void ath11k_nss_vdev_delete(struct ath11k_vif *arvif); + int ath11k_nss_vdev_up(struct ath11k_vif *arvif); +@@ -219,7 +227,8 @@ static inline int ath11k_nss_tx(struct a + return 0; + } + +-static inline int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, int cmd, int val) ++static inline int ath11k_nss_vdev_set_cmd(struct ath11k_vif *arvif, enum ath11k_nss_vdev_cmd cmd, ++ int val) + { + return 0; + } +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -104,8 +104,10 @@ static void ath11k_init_wmi_config_qca63 + + static void ath11k_hw_ipq8074_reo_setup(struct ath11k_base *ab) + { ++ u8 frag_dest_ring = HAL_SRNG_RING_ID_REO2SW1; + u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + u32 val; ++ + /* Each hash entry uses three bits to map to a particular ring. */ + u32 ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 3 | +@@ -116,11 +118,14 @@ static void ath11k_hw_ipq8074_reo_setup( + HAL_HASH_ROUTING_RING_SW3 << 18 | + HAL_HASH_ROUTING_RING_SW4 << 21; + ++ if (ab->nss.enabled) ++ frag_dest_ring = HAL_SRNG_REO_ALTERNATE_SELECT; ++ + val = ath11k_hif_read32(ab, reo_base + HAL_REO1_GEN_ENABLE); + + val &= ~HAL_REO1_GEN_ENABLE_FRAG_DST_RING; + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_FRAG_DST_RING, +- HAL_SRNG_RING_ID_REO2SW1) | ++ frag_dest_ring) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); + ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); +@@ -134,6 +139,10 @@ static void ath11k_hw_ipq8074_reo_setup( + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab), + HAL_DEFAULT_REO_TIMEOUT_USEC); + ++ /* REO Dest ring setup is not required in NSS offload case */ ++ if (ab->nss.enabled) ++ return; ++ + ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, + FIELD_PREP(HAL_REO_DEST_RING_CTRL_HASH_RING_MAP, + ring_hash_map)); +@@ -758,8 +767,10 @@ static u8 *ath11k_hw_wcn6855_rx_desc_mpd + + static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab) + { ++ u8 frag_dest_ring = HAL_SRNG_RING_ID_REO2SW1; + u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG; + u32 val; ++ + /* Each hash entry uses four bits to map to a particular ring. */ + u32 ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 | + HAL_HASH_ROUTING_RING_SW2 << 4 | +@@ -770,6 +781,9 @@ static void ath11k_hw_wcn6855_reo_setup( + HAL_HASH_ROUTING_RING_SW3 << 24 | + HAL_HASH_ROUTING_RING_SW4 << 28; + ++ if (ab->nss.enabled) ++ frag_dest_ring = HAL_SRNG_REO_ALTERNATE_SELECT; ++ + val = ath11k_hif_read32(ab, reo_base + HAL_REO1_GEN_ENABLE); + val |= FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE, 1) | + FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); +@@ -777,7 +791,7 @@ static void ath11k_hw_wcn6855_reo_setup( + + val = ath11k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTL(ab)); + val &= ~HAL_REO1_MISC_CTL_FRAGMENT_DST_RING; +- val |= FIELD_PREP(HAL_REO1_MISC_CTL_FRAGMENT_DST_RING, HAL_SRNG_RING_ID_REO2SW1); ++ val |= FIELD_PREP(HAL_REO1_MISC_CTL_FRAGMENT_DST_RING, frag_dest_ring); + ath11k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTL(ab), val); + + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab), +@@ -789,6 +803,10 @@ static void ath11k_hw_wcn6855_reo_setup( + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab), + HAL_DEFAULT_REO_TIMEOUT_USEC); + ++ /* REO Dest ring setup is not required in NSS offload case */ ++ if (ab->nss.enabled) ++ return; ++ + ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2, + ring_hash_map); + ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -571,6 +571,12 @@ static int ath11k_pcic_ext_irq_config(st + netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + ath11k_pcic_ext_grp_napi_poll); + ++ /* tcl, reo, rx_err, wbm release, rxdma rings are offloaded to nss. */ ++ if (ab->nss.enabled && ++ !(ab->hw_params.ring_mask->reo_status[i] || ++ ab->hw_params.ring_mask->rx_mon_status[i])) ++ continue; ++ + if (ab->hw_params.ring_mask->tx[i] || + ab->hw_params.ring_mask->rx[i] || + ab->hw_params.ring_mask->rx_err[i] || +--- a/local-symbols ++++ b/local-symbols +@@ -170,6 +170,7 @@ WCN36XX_DEBUGFS= + ATH11K= + ATH11K_AHB= + ATH11K_PCI= ++ATH11K_NSS_SUPPORT= + ATH11K_DEBUG= + ATH11K_DEBUGFS= + ATH11K_TRACING= diff --git a/package/kernel/mac80211/patches/nss/ath11k/203-mac80211-ath11k-fw-dynamic-muedca.patch b/package/kernel/mac80211/patches/nss/ath11k/203-mac80211-ath11k-fw-dynamic-muedca.patch new file mode 100644 index 00000000000000..a91d4aa97cbbca --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/203-mac80211-ath11k-fw-dynamic-muedca.patch @@ -0,0 +1,162 @@ +From ed838800bb8f4c59b320395066ac356f74528a50 Mon Sep 17 00:00:00 2001 +From: Muna Sinada +Date: Wed, 29 Jul 2020 00:11:30 -0700 +Subject: [PATCH] 203-mac80211-ath11k-fw-dynamic-muedca.patch + +mac80211/ath11k:FW Initiated Dynamic MU-EDCA + +Implementing the updating of firmware initiated dynamic MU-EDCA +parameters in Beacon IE. Firmware routinely checks its clients and +updates its MU-EDCA values every 3 seconds. Firmware is tuning +MU-EDCA parameters to improve performance. As part of this process, +the firmware informs host about new MU-EDCA values utilizing +WMI_MUEDCA_PARAMS_CONFIG_EVENTID. FW expectation is that host will +update MU-EDCA parameters in the Beacon IE. +Implementation consists of: + (1) Receiving updated parameters through event in ATH11k + (2) Passing updated parameters ATH11k -> mac80211 -> cfg80211 + (3) Passing updated parameters to user space. + +Signed-off-by: Muna Sinada +--- + drivers/net/wireless/ath/ath11k/wmi.c | 97 +++++++++++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/wmi.h | 12 +++++ + include/net/cfg80211.h | 11 ++++ + include/net/mac80211.h | 13 +++++ + include/uapi/linux/nl80211.h | 10 ++++ + net/mac80211/mlme.c | 12 +++++ + net/mac80211/trace.h | 20 ++++++++ + net/wireless/nl80211.c | 36 +++++++++++++ + 8 files changed, 200 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -148,6 +148,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_vdev_delete_resp_event) }, + [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { + .min_len = sizeof(struct wmi_obss_color_collision_event) }, ++ [WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT] = { ++ .min_len = sizeof(struct wmi_pdev_update_muedca_event) }, + [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { + .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { +@@ -8727,6 +8729,74 @@ exit: + kfree(tb); + } + ++static void ++ath11k_wmi_pdev_update_muedca_params_status_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ const void **tb; ++ const struct wmi_pdev_update_muedca_event *ev; ++ struct ieee80211_mu_edca_param_set *params; ++ struct ath11k *ar; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return; ++ } ++ ++ ev = tb[WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT]; ++ if (!ev) { ++ ath11k_warn(ab, "failed to fetch pdev update muedca params ev"); ++ goto exit; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "Update MU-EDCA parameters for pdev:%d\n", ev->pdev_id); ++ ++ ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); ++ if (!ar) { ++ ath11k_warn(ab, ++ "MU-EDCA parameter change in invalid pdev %d\n", ++ ev->pdev_id); ++ goto exit; ++ } ++ ++ params = kzalloc(sizeof(*params), GFP_ATOMIC); ++ if (!params) { ++ ath11k_warn(ab, ++ "Failed to allocate memory for updated MU-EDCA Parameters"); ++ goto exit; ++ } ++ ++ params->ac_be.aifsn = ev->aifsn[0]; ++ params->ac_be.ecw_min_max = ((0xF & ev->ecwmax[0]) << 4) | ++ (0xF & ev->ecwmin[0]); ++ params->ac_be.mu_edca_timer = ev->muedca_expiration_time[0]; ++ ++ params->ac_bk.aifsn = ev->aifsn[1]; ++ params->ac_bk.ecw_min_max = ((0xF & ev->ecwmax[1]) << 4) | ++ (0xF & ev->ecwmin[1]); ++ params->ac_bk.mu_edca_timer = ev->muedca_expiration_time[1]; ++ ++ params->ac_vi.aifsn = ev->aifsn[2]; ++ params->ac_vi.ecw_min_max = ((0xF & ev->ecwmax[2]) << 4) | ++ (0xF & ev->ecwmin[2]); ++ params->ac_vi.mu_edca_timer = ev->muedca_expiration_time[2]; ++ ++ params->ac_vo.aifsn = ev->aifsn[3]; ++ params->ac_vo.ecw_min_max = ((0xF & ev->ecwmax[3]) << 4) | ++ (0xF & ev->ecwmin[3]); ++ params->ac_vo.mu_edca_timer = ev->muedca_expiration_time[3]; ++ ++ ieee80211_update_muedca_params(ar->hw, params, GFP_ATOMIC); ++ ++ kfree(params); ++exit: ++ kfree(tb); ++} ++ + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) + { + struct wmi_cmd_hdr *cmd_hdr; +@@ -8845,6 +8915,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_11D_NEW_COUNTRY_EVENTID: + ath11k_reg_11d_new_cc_event(ab, skb); + break; ++ case WMI_MUEDCA_PARAMS_CONFIG_EVENTID: ++ ath11k_wmi_pdev_update_muedca_params_status_event(ab, skb); ++ break; + case WMI_DIAG_EVENTID: + ath11k_wmi_diag_event(ab, skb); + break; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -759,6 +759,7 @@ enum wmi_tlv_event_id { + WMI_READ_DATA_FROM_FLASH_EVENTID, + WMI_REPORT_RX_AGGR_FAILURE_EVENTID, + WMI_PKGID_EVENTID, ++ WMI_MUEDCA_PARAMS_CONFIG_EVENTID = 0x1d01e, + WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO), + WMI_UPLOADH_EVENTID, + WMI_CAPTUREH_EVENTID, +@@ -1869,6 +1870,7 @@ enum wmi_tlv_tag { + WMI_TAG_NDP_EVENT, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_CMD = 0x301, + WMI_TAG_PDEV_PEER_PKTLOG_FILTER_INFO, ++ WMI_TAG_MUEDCA_PARAMS_CONFIG_EVENT = 0x32a, + WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344, + WMI_TAG_PDEV_SRG_BSS_COLOR_BITMAP_CMD = 0x37b, + WMI_TAG_PDEV_SRG_PARTIAL_BSSID_BITMAP_CMD, +@@ -4868,6 +4870,16 @@ struct wmi_pdev_temperature_event { + u32 pdev_id; + } __packed; + ++#define WMI_AC_MAX 4 ++ ++struct wmi_pdev_update_muedca_event { ++ u32 pdev_id; ++ u32 aifsn[WMI_AC_MAX]; ++ u32 ecwmin[WMI_AC_MAX]; ++ u32 ecwmax[WMI_AC_MAX]; ++ u32 muedca_expiration_time[WMI_AC_MAX]; ++} __packed; ++ + #define WMI_RX_STATUS_OK 0x00 + #define WMI_RX_STATUS_ERR_CRC 0x01 + #define WMI_RX_STATUS_ERR_DECRYPT 0x08 diff --git a/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Add-support-for-dynamic-vlan.patch b/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Add-support-for-dynamic-vlan.patch new file mode 100644 index 00000000000000..a765d1a292c60c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Add-support-for-dynamic-vlan.patch @@ -0,0 +1,381 @@ +From 71add81f4a3f1ea505f498d789e7a1721c4d7a6e Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Mon, 4 Sep 2023 12:48:19 +0530 +Subject: [PATCH] ath11k: Add support for dynamic vlan + +This patch adds support for dynamic vlan. VLAN group traffics +are encrypted in software. vlan unicast packets shall be taking +8023 xmit path if encap offload is enabled and mcast/bcast will +be using 80211 xmit path. + +Metadata info in dp_tx added to notify firmware that the +multicast/broadcast packets are encrypted in sw. + +Signed-off-by: Seevalamuthu Mariappan +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/dp_tx.c | 80 +++++++++- + drivers/net/wireless/ath/ath11k/dp_tx.h | 198 ++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/mac.c | 3 + + 4 files changed, 279 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -119,6 +119,7 @@ struct ath11k_skb_cb { + u32 cipher; + struct ath11k *ar; + struct ieee80211_vif *vif; ++ u32 pkt_offset; + } __packed; + + struct ath11k_skb_rxcb { +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -79,6 +79,43 @@ enum hal_encrypt_type ath11k_dp_tx_get_e + } + } + ++#define HTT_META_DATA_ALIGNMENT 0x8 ++ ++static int ath11k_dp_metadata_align_skb(struct sk_buff *skb, u8 align_len) ++{ ++ if (unlikely(skb_cow_head(skb, align_len))) ++ return -ENOMEM; ++ ++ skb_push(skb, align_len); ++ memset(skb->data, 0, align_len); ++ return 0; ++} ++ ++static int ath11k_dp_prepare_htt_metadata(struct sk_buff *skb, ++ u8 *htt_metadata_size) ++{ ++ u8 htt_desc_size; ++ /* Size rounded of multiple of 8 bytes */ ++ u8 htt_desc_size_aligned; ++ int ret; ++ struct htt_tx_msdu_desc_ext *desc_ext; ++ ++ htt_desc_size = sizeof(struct htt_tx_msdu_desc_ext); ++ htt_desc_size_aligned = ALIGN(htt_desc_size, HTT_META_DATA_ALIGNMENT); ++ ++ ret = ath11k_dp_metadata_align_skb(skb, htt_desc_size_aligned); ++ if (unlikely(ret)) ++ return ret; ++ ++ desc_ext = (struct htt_tx_msdu_desc_ext *)skb->data; ++ desc_ext->valid_encrypt_type = 1; ++ desc_ext->encrypt_type = 0; ++ desc_ext->host_tx_desc_pool = 1; ++ *htt_metadata_size = htt_desc_size_aligned; ++ ++ return 0; ++} ++ + int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, + struct ath11k_sta *arsta, struct sk_buff *skb) + { +@@ -96,7 +133,8 @@ int ath11k_dp_tx(struct ath11k *ar, stru + int ret; + u32 ring_selector = 0; + u8 ring_map = 0; +- bool tcl_ring_retry; ++ bool tcl_ring_retry, is_diff_encap = false; ++ u8 align_pad, htt_meta_size = 0; + + if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))) + return -ESHUTDOWN; +@@ -189,7 +227,10 @@ tcl_ring_sel: + + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: +- ath11k_dp_tx_encap_nwifi(skb); ++ if (arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ++ is_diff_encap = true; ++ else ++ ath11k_dp_tx_encap_nwifi(skb); + break; + case HAL_TCL_ENCAP_TYPE_RAW: + if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) { +@@ -208,6 +249,33 @@ tcl_ring_sel: + goto fail_remove_idr; + } + ++ /* Add metadata for sw encrypted vlan group traffic */ ++ if ((!test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) && ++ !(info->control.flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && ++ !info->control.hw_key && ieee80211_has_protected(hdr->frame_control)) || ++ (skb->protocol == cpu_to_be16(ETH_P_PAE) && is_diff_encap)) { ++ /* HW requirement is that metadata should always point to a ++ * 8-byte aligned address. So we add alignment pad to start of ++ * buffer. HTT Metadata should be ensured to be multiple of 8-bytes ++ * to get 8-byte aligned start address along with align_pad added ++ */ ++ align_pad = ((unsigned long)skb->data) & (HTT_META_DATA_ALIGNMENT - 1); ++ ret = ath11k_dp_metadata_align_skb(skb, align_pad); ++ if (unlikely(ret)) ++ goto fail_remove_idr; ++ ++ ti.pkt_offset += align_pad; ++ ret = ath11k_dp_prepare_htt_metadata(skb, &htt_meta_size); ++ if (unlikely(ret)) ++ goto fail_remove_idr; ++ ++ ti.pkt_offset += htt_meta_size; ++ ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT; ++ ti.flags0 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_TO_FW, 1); ++ ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW; ++ ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; ++ } ++ + ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, ti.paddr))) { + atomic_inc(&ab->soc_stats.tx_err.misc_fail); +@@ -216,7 +284,8 @@ tcl_ring_sel: + goto fail_remove_idr; + } + +- ti.data_len = skb->len; ++ ti.data_len = skb->len - ti.pkt_offset; ++ skb_cb->pkt_offset = ti.pkt_offset; + skb_cb->paddr = ti.paddr; + skb_cb->vif = arvif->vif; + skb_cb->ar = ar; +@@ -272,6 +341,8 @@ fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); + + fail_remove_idr: ++ if (ti.pkt_offset) ++ skb_pull(skb, ti.pkt_offset); + spin_lock_bh(&tx_ring->tx_idr_lock); + idr_remove(&tx_ring->txbuf_idr, + FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id)); +@@ -348,6 +419,9 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + return; + } + ++ if (skb_cb->pkt_offset) ++ skb_pull(msdu, skb_cb->pkt_offset); /* removing the alignment and htt meta data */ ++ + memset(&info->status, 0, sizeof(info->status)); + + if (ts->acked) { +--- a/drivers/net/wireless/ath/ath11k/dp_tx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.h +@@ -16,6 +16,204 @@ struct ath11k_dp_htt_wbm_tx_status { + u16 peer_id; + }; + ++/* htt_tx_msdu_desc_ext ++ * ++ * valid_pwr ++ * if set, tx pwr spec is valid ++ * ++ * valid_mcs_mask ++ * if set, tx MCS mask is valid ++ * ++ * valid_nss_mask ++ * if set, tx Nss mask is valid ++ * ++ * valid_preamble_type ++ * if set, tx preamble spec is valid ++ * ++ * valid_retries ++ * if set, tx retries spec is valid ++ * ++ * valid_bw_info ++ * if set, tx dyn_bw and bw_mask are valid ++ * ++ * valid_guard_interval ++ * if set, tx guard intv spec is valid ++ * ++ * valid_chainmask ++ * if set, tx chainmask is valid ++ * ++ * valid_encrypt_type ++ * if set, encrypt type is valid ++ * ++ * valid_key_flags ++ * if set, key flags is valid ++ * ++ * valid_expire_tsf ++ * if set, tx expire TSF spec is valid ++ * ++ * valid_chanfreq ++ * if set, chanfreq is valid ++ * ++ * is_dsrc ++ * if set, MSDU is a DSRC frame ++ * ++ * guard_interval ++ * 0.4us, 0.8us, 1.6us, 3.2us ++ * ++ * encrypt_type ++ * 0 = NO_ENCRYPT, ++ * 1 = ENCRYPT, ++ * 2 ~ 3 - Reserved ++ * ++ * retry_limit ++ * Specify the maximum number of transmissions, including the ++ * initial transmission, to attempt before giving up if no ack ++ * is received. ++ * If the tx rate is specified, then all retries shall use the ++ * same rate as the initial transmission. ++ * If no tx rate is specified, the target can choose whether to ++ * retain the original rate during the retransmissions, or to ++ * fall back to a more robust rate. ++ * ++ * use_dcm_11ax ++ * If set, Use Dual subcarrier modulation. ++ * Valid only for 11ax preamble types HE_SU ++ * and HE_EXT_SU ++ * ++ * ltf_subtype_11ax ++ * Takes enum values of htt_11ax_ltf_subtype_t ++ * Valid only for 11ax preamble types HE_SU ++ * and HE_EXT_SU ++ * ++ * dyn_bw ++ * 0 = static bw, 1 = dynamic bw ++ * ++ * bw_mask ++ * Valid only if dyn_bw == 0 (static bw). ++ * ++ * host_tx_desc_pool ++ * If set, Firmware allocates tx_descriptors ++ * in WAL_BUFFERID_TX_HOST_DATA_EXP,instead ++ * of WAL_BUFFERID_TX_TCL_DATA_EXP. ++ * Use cases: ++ * Any time firmware uses TQM-BYPASS for Data ++ * TID, firmware expect host to set this bit. ++ * ++ * power ++ * unit of the power field is 0.5 dbm ++ * signed value ranging from -64dbm to 63.5 dbm ++ * ++ * mcs_mask ++ * mcs bit mask of 0 ~ 11 ++ * Setting more than one MCS isn't currently ++ * supported by the target (but is supported ++ * in the interface in case in the future ++ * the target supports specifications of ++ * a limited set of MCS values. ++ * ++ * nss_mask ++ * Nss bit mask 0 ~ 7 ++ * Setting more than one Nss isn't currently ++ * supported by the target (but is supported ++ * in the interface in case in the future ++ * the target supports specifications of ++ * a limited set of Nss values. ++ * ++ * pream_type ++ * Preamble types ++ * ++ * update_peer_cache ++ * When set these custom values will be ++ * used for all packets, until the next ++ * update via this ext header. ++ * This is to make sure not all packets ++ * need to include this header. ++ * ++ * chain_mask ++ * specify which chains to transmit from ++ * ++ * key_flags ++ * Key Index and related flags - used in mesh mode ++ * ++ * chanfreq ++ * Channel frequency: This identifies the desired channel ++ * frequency (in MHz) for tx frames. This is used by FW to help ++ * determine when it is safe to transmit or drop frames for ++ * off-channel operation. ++ * The default value of zero indicates to FW that the corresponding ++ * VDEV's home channel (if there is one) is the desired channel ++ * frequency. ++ * ++ * expire_tsf_lo ++ * tx expiry time (TSF) LSBs ++ * ++ * expire_tsf_hi ++ * tx expiry time (TSF) MSBs ++ * ++ * learning_frame ++ * When this flag is set, this frame will be dropped by FW ++ * rather than being enqueued to the Transmit Queue Manager (TQM) HW. ++ * ++ * send_as_standalone ++ * This will indicate if the msdu needs to be sent as a singleton PPDU, ++ * i.e. with no A-MSDU or A-MPDU aggregation. ++ * The scope is extended to other use-cases. ++ * ++ * is_host_opaque_valid ++ * set this bit to 1 if the host_opaque_cookie is populated ++ * with valid information. ++ * ++ * host_opaque_cookie ++ * Host opaque cookie for special frames ++ */ ++ ++struct htt_tx_msdu_desc_ext { ++ u32 ++ valid_pwr : 1, ++ valid_mcs_mask : 1, ++ valid_nss_mask : 1, ++ valid_preamble_type : 1, ++ valid_retries : 1, ++ valid_bw_info : 1, ++ valid_guard_interval : 1, ++ valid_chainmask : 1, ++ valid_encrypt_type : 1, ++ valid_key_flags : 1, ++ valid_expire_tsf : 1, ++ valid_chanfreq : 1, ++ is_dsrc : 1, ++ guard_interval : 2, ++ encrypt_type : 2, ++ retry_limit : 4, ++ use_dcm_11ax : 1, ++ ltf_subtype_11ax : 2, ++ dyn_bw : 1, ++ bw_mask : 6, ++ host_tx_desc_pool : 1; ++ u32 ++ power : 8, ++ mcs_mask : 12, ++ nss_mask : 8, ++ pream_type : 3, ++ update_peer_cache : 1; ++ u32 ++ chain_mask : 8, ++ key_flags : 8, ++ chanfreq : 16; ++ ++ u32 expire_tsf_lo; ++ u32 expire_tsf_hi; ++ ++ u32 ++ learning_frame : 1, ++ send_as_standalone : 1, ++ is_host_opaque_valid : 1, ++ rsvd0 : 29; ++ u32 ++ host_opaque_cookie : 16, ++ rsvd1 : 16; ++} __packed; ++ + void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts); + int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab); + int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -9771,6 +9771,9 @@ static int __ath11k_mac_register(struct + */ + ar->hw->wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR); + ++ ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); ++ ar->hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); ++ + /* Apply the regd received during initialization */ + ret = ath11k_regd_update(ar); + if (ret) { diff --git a/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Enable-256_512MB-profiles.patch b/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Enable-256_512MB-profiles.patch new file mode 100644 index 00000000000000..4d9c1537a8bb4b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Enable-256_512MB-profiles.patch @@ -0,0 +1,331 @@ +From 1b402e444ff99efe84d09a084b96c39826783a8e Mon Sep 17 00:00:00 2001 +From: Ramya Gnanasekar +Date: Thu, 10 Sep 2020 13:33:55 +0530 +Subject: [PATCH] ath11k: Enable 512MB profile in ath11k + +Below changes are made to enable 512MB mem mode in ath11k + * Makefile changes to implement compilation flag when + 512MB mem profile is configured. + * Enabling 512MB mem profile by default from Makefile + for IPQ5018. This can be removed later once + 512MB profile config is supported. + * Update target_mem_mode, number of stations, peer and vap + during compile time + +Signed-off-by: Ramya Gnanasekar +--- + drivers/net/wireless/ath/ath11k/Kconfig | 7 +++++++ + drivers/net/wireless/ath/ath11k/hw.h | 14 +++++++++++--- + drivers/net/wireless/ath/ath11k/qmi.c | 2 +- + drivers/net/wireless/ath/ath11k/qmi.h | 6 +++++- + 4 files changed, 24 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -23,6 +23,20 @@ config ATH11K_NSS_SUPPORT + + If unsure, say Y to enable NSS offload support. + ++config ATH11K_MEM_PROFILE_512M ++ bool "ath11k enable 512MB memory profile" ++ depends on ATH11K ++ default n ++ ---help--- ++ Enables 512MB memory profile for ath11k ++ ++config ATH11K_MEM_PROFILE_256M ++ bool "ath11k enable 256MB memory profile" ++ depends on ATH11K ++ default n ++ ---help--- ++ Enables 256MB memory profile for ath11k ++ + config ATH11K_AHB + tristate "Atheros ath11k AHB support" + depends on m +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -11,11 +11,29 @@ + #include "wmi.h" + + /* Target configuration defines */ ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M + ++#define TARGET_NUM_VDEVS(ab) 8 ++#define TARGET_NUM_PEERS_PDEV(ab) (128 + TARGET_NUM_VDEVS(ab)) ++/* Max num of stations (per radio) */ ++#define TARGET_NUM_STATIONS(ab) 128 ++#define ATH11K_QMI_TARGET_MEM_MODE ATH11K_QMI_TARGET_MEM_MODE_512M ++#define ATH11K_DP_TX_COMP_RING_SIZE 8192 ++#define ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE 512 ++#define ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE 128 ++#define ATH11K_DP_RXDMA_MONITOR_DST_RING_SIZE 128 ++#else + /* Num VDEVS per radio */ +-#define TARGET_NUM_VDEVS(ab) (ab->hw_params.num_vdevs) +- +-#define TARGET_NUM_PEERS_PDEV(ab) (ab->hw_params.num_peers + TARGET_NUM_VDEVS(ab)) ++#define TARGET_NUM_VDEVS(ab) (ab->hw_params.num_vdevs_peers[ab->qmi.target_mem_mode].num_vdevs) ++#define TARGET_NUM_PEERS_PDEV(ab) (ab->hw_params.num_vdevs_peers[ab->qmi.target_mem_mode].num_peers + TARGET_NUM_VDEVS(ab)) ++/* Max num of stations (per radio) */ ++#define TARGET_NUM_STATIONS(ab) (ab->hw_params.num_vdevs_peers[ab->qmi.target_mem_mode].num_peers) ++#define ATH11K_QMI_TARGET_MEM_MODE ATH11K_QMI_TARGET_MEM_MODE_DEFAULT ++#define ATH11K_DP_TX_COMP_RING_SIZE 32768 ++#define ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE 1024 ++#define ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 ++#define ATH11K_DP_RXDMA_MONITOR_DST_RING_SIZE 2048 ++#endif + + /* Num of peers for Single Radio mode */ + #define TARGET_NUM_PEERS_SINGLE(ab) (TARGET_NUM_PEERS_PDEV(ab)) +@@ -26,9 +44,6 @@ + /* Num of peers for DBS_SBS */ + #define TARGET_NUM_PEERS_DBS_SBS(ab) (3 * TARGET_NUM_PEERS_PDEV(ab)) + +-/* Max num of stations (per radio) */ +-#define TARGET_NUM_STATIONS(ab) (ab->hw_params.num_peers) +- + #define TARGET_NUM_PEERS(ab, x) TARGET_NUM_PEERS_##x(ab) + #define TARGET_NUM_PEER_KEYS 2 + #define TARGET_NUM_TIDS(ab, x) (2 * TARGET_NUM_PEERS(ab, x) + \ +@@ -226,6 +241,7 @@ struct ath11k_hw_params { + u32 tx_ring_size; + bool smp2p_wow_exit; + bool support_fw_mac_sequence; ++ const struct ath11k_num_vdevs_peers *num_vdevs_peers; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -29,6 +29,12 @@ + #define ATH11K_QMI_BDF_EXT_STR_LENGTH 0x20 + #define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 5 + ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++#define ATH11K_QMI_IPQ8074_M3_DUMP_ADDRESS 0x4E800000 ++#else ++#define ATH11K_QMI_IPQ8074_M3_DUMP_ADDRESS 0x51000000 ++#endif ++ + #define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 + #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 + #define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x003E +@@ -42,6 +48,11 @@ + #define ATH11K_QMI_DEVICE_BAR_SIZE 0x200000 + + struct ath11k_base; ++enum ath11k_target_mem_mode { ++ ATH11K_QMI_TARGET_MEM_MODE_DEFAULT = 0, ++ ATH11K_QMI_TARGET_MEM_MODE_512M, ++ ATH11K_QMI_TARGET_MEM_MODE_256M, ++}; + + enum ath11k_qmi_file_type { + ATH11K_QMI_FILE_TYPE_BDF_GOLDEN, +--- a/local-symbols ++++ b/local-symbols +@@ -171,6 +171,8 @@ ATH11K= + ATH11K_AHB= + ATH11K_PCI= + ATH11K_NSS_SUPPORT= ++ATH11K_MEM_PROFILE_256M= ++ATH11K_MEM_PROFILE_512M= + ATH11K_DEBUG= + ATH11K_DEBUGFS= + ATH11K_TRACING= +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -887,6 +887,11 @@ struct ath11k_msi_config { + u16 hw_rev; + }; + ++struct ath11k_num_vdevs_peers { ++ u32 num_vdevs; ++ u32 num_peers; ++}; ++ + /* Master structure to hold the hw data which may be used in core module */ + struct ath11k_base { + enum ath11k_hw_rev hw_rev; +@@ -1032,6 +1037,8 @@ struct ath11k_base { + const struct ath11k_pci_ops *ops; + } pci; + ++ atomic_t num_max_allowed; ++ + #ifdef CPTCFG_NL80211_TESTMODE + struct { + u32 data_pos; +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -206,8 +206,9 @@ struct ath11k_pdev_dp { + #define DP_WBM_RELEASE_RING_SIZE 64 + #define DP_TCL_DATA_RING_SIZE 512 + #define DP_TCL_DATA_RING_SIZE_WCN6750 2048 +-#define DP_TX_COMP_RING_SIZE 32768 ++#define DP_TX_COMP_RING_SIZE ATH11K_DP_TX_COMP_RING_SIZE + #define DP_TX_IDR_SIZE DP_TX_COMP_RING_SIZE ++#define DP_TX_COMP_MAX_ALLOWED DP_TX_COMP_RING_SIZE + #define DP_TCL_CMD_RING_SIZE 32 + #define DP_TCL_STATUS_RING_SIZE 32 + #define DP_REO_DST_RING_MAX 4 +@@ -220,9 +221,9 @@ struct ath11k_pdev_dp { + #define DP_RXDMA_BUF_RING_SIZE 4096 + #define DP_RXDMA_REFILL_RING_SIZE 2048 + #define DP_RXDMA_ERR_DST_RING_SIZE 1024 +-#define DP_RXDMA_MON_STATUS_RING_SIZE 1024 +-#define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 +-#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 ++#define DP_RXDMA_MON_STATUS_RING_SIZE ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE ++#define DP_RXDMA_MONITOR_BUF_RING_SIZE ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE ++#define DP_RXDMA_MONITOR_DST_RING_SIZE ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE + #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 + + #define DP_RX_RELEASE_RING_NUM 3 +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -334,6 +334,7 @@ tcl_ring_sel: + skb->data, skb->len); + + atomic_inc(&ar->dp.num_tx_pending); ++ atomic_inc(&ab->num_max_allowed); + + return 0; + +@@ -380,6 +381,7 @@ static void ath11k_dp_tx_free_txbuf(stru + ar = ab->pdevs[mac_id].ar; + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); ++ atomic_dec(&ab->num_max_allowed); + } + + static void +@@ -411,6 +413,7 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); ++ atomic_dec(&ab->num_max_allowed); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + +@@ -825,6 +828,7 @@ void ath11k_dp_tx_completion_handler(str + wake_up(&ar->dp.tx_empty_waitq); + + ath11k_dp_tx_complete_msdu(ar, msdu, &ts); ++ atomic_dec(&ab->num_max_allowed); + } + } + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -42,6 +42,8 @@ bool ath11k_ftm_mode; + module_param_named(ftm_mode, ath11k_ftm_mode, bool, 0444); + MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode"); + ++static const struct ath11k_num_vdevs_peers ath11k_vdevs_peers[]; ++ + static struct ath11k_hw_params ath11k_hw_params[] = { + { + .hw_rev = ATH11K_HW_IPQ8074, +@@ -127,6 +129,7 @@ static struct ath11k_hw_params ath11k_hw + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .num_vdevs_peers = ath11k_vdevs_peers, + }, + { + .hw_rev = ATH11K_HW_IPQ6018_HW10, +@@ -177,7 +180,7 @@ static struct ath11k_hw_params ath11k_hw + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = true, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, +@@ -259,7 +262,7 @@ static struct ath11k_hw_params ath11k_hw + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -426,7 +429,7 @@ static struct ath11k_hw_params ath11k_hw + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -462,6 +465,7 @@ static struct ath11k_hw_params ath11k_hw + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, ++ .num_vdevs_peers = ath11k_vdevs_peers, + }, + { + .name = "wcn6855 hw2.1", +@@ -509,7 +513,7 @@ static struct ath11k_hw_params ath11k_hw + .coldboot_cal_mm = false, + .coldboot_cal_ftm = false, + .cbcal_restart_fw = false, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -545,6 +549,7 @@ static struct ath11k_hw_params ath11k_hw + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = true, ++ .num_vdevs_peers = ath11k_vdevs_peers, + }, + { + .name = "wcn6750 hw1.0", +@@ -593,7 +598,7 @@ static struct ath11k_hw_params ath11k_hw + .coldboot_cal_mm = true, + .coldboot_cal_ftm = true, + .cbcal_restart_fw = false, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = false, +@@ -672,7 +677,7 @@ static struct ath11k_hw_params ath11k_hw + .supports_monitor = false, + .supports_sta_ps = false, + .supports_shadow_regs = false, +- .fw_mem_mode = 0, ++ .fw_mem_mode = ATH11K_QMI_TARGET_MEM_MODE, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_regdb = false, +@@ -707,6 +712,22 @@ static struct ath11k_hw_params ath11k_hw + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, ++ .num_vdevs_peers = ath11k_vdevs_peers, ++ }, ++}; ++ ++static const struct ath11k_num_vdevs_peers ath11k_vdevs_peers[] = { ++ { ++ .num_vdevs = (16 + 1), ++ .num_peers = 512, ++ }, ++ { ++ .num_vdevs = (8 + 1), ++ .num_peers = 128, ++ }, ++ { ++ .num_vdevs = 8, ++ .num_peers = 128, + }, + }; + diff --git a/package/kernel/mac80211/patches/nss/ath11k/211-001-ath11k-add-WDS-offload-changes-to-NSS-driver-interface.patch b/package/kernel/mac80211/patches/nss/ath11k/211-001-ath11k-add-WDS-offload-changes-to-NSS-driver-interface.patch new file mode 100644 index 00000000000000..a470ba934507f1 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/211-001-ath11k-add-WDS-offload-changes-to-NSS-driver-interface.patch @@ -0,0 +1,688 @@ +From d4c3b17e66243a2d6d8845192453ef7da568bac2 Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Thu, 10 Sep 2020 15:58:53 +0530 +Subject: [PATCH 1/2] ath11k: add WDS offload changes to NSS driver interface + +add WDS and MEC AST handling to NSS + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/nss.c | 465 ++++++++++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/nss.h | 36 ++- + 2 files changed, 473 insertions(+), 28 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -307,6 +307,22 @@ void ath11k_nss_wifili_event_receive(str + case NSS_WIFILI_TID_REOQ_SETUP_MSG: + /* TODO setup tidq */ + break; ++ case NSS_WIFILI_WDS_PEER_ADD_MSG: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili wds peer add event received %d response %d error %d\n", ++ msg_type, response, error); ++ break; ++ case NSS_WIFILI_WDS_PEER_UPDATE_MSG: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili wds peer update event received %d response %d error %d\n", ++ msg_type, response, error); ++ break; ++ case NSS_WIFILI_WDS_PEER_MAP_MSG: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili wds peer map event received %d response %d error %d\n", ++ msg_type, response, error); ++ break; ++ case NSS_WIFILI_WDS_PEER_DEL_MSG: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili wds peer del event received %d response %d error %d\n", ++ msg_type, response, error); ++ break; + default: + ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type); + break; +@@ -417,13 +433,6 @@ static void ath11k_nss_vdev_event_receiv + /*TODO*/ + } + +-static void +-ath11k_nss_vdev_special_data_receive(struct net_device *dev, struct sk_buff *skb, +- __attribute__((unused)) struct napi_struct *napi) +-{ +- /* TODO */ +-} +- + /* TODO: move to mac80211 after cleanups/refactoring required after feature completion */ + static int ath11k_nss_deliver_rx(struct ieee80211_vif *vif, struct sk_buff *skb, + bool eth, int data_offs, struct napi_struct *napi) +@@ -547,11 +556,239 @@ static int ath11k_nss_undecap_nwifi(stru + return 0; + } + ++static void ath11k_nss_wds_type_rx(struct ath11k *ar, u8* src_mac, u8 is_sa_valid, ++ u8 addr4_valid, u16 peer_id) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_ast_entry *ast_entry = NULL; ++ struct ath11k_peer *ta_peer = NULL; ++ ++ spin_lock_bh(&ab->base_lock); ++ ta_peer = ath11k_peer_find_by_id(ab, peer_id); ++ ++ if (!ta_peer) { ++ spin_unlock_bh(&ab->base_lock); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS,"ath11k_nss_wds_type_rx ta_peer %pM\n", ++ ta_peer->addr); ++ ++ if (addr4_valid) { ++ ast_entry = ath11k_peer_ast_find_by_addr(ab, src_mac); ++ if (!is_sa_valid) { ++ ath11k_peer_add_ast(ar, ta_peer, src_mac, ++ ATH11K_AST_TYPE_WDS); ++ ath11k_nss_add_wds_peer(ar, ta_peer, ++ src_mac, ATH11K_AST_TYPE_WDS); ++ } else { ++ if (!ast_entry) { ++ ath11k_peer_add_ast(ar, ta_peer, src_mac, ++ ATH11K_AST_TYPE_WDS); ++ ath11k_nss_add_wds_peer(ar, ta_peer, src_mac, ++ ATH11K_AST_TYPE_WDS); ++ } else { ++ ath11k_peer_update_ast(ar, ta_peer, ast_entry); ++ ath11k_nss_update_wds_peer(ar, ta_peer, src_mac); ++ } ++ } ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++} ++ ++static void ath11k_nss_mec_handler(struct ath11k *ar, u8* mec_mac_addr) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_peer *peer = ar->bss_peer; ++ u8 mac_addr[ETH_ALEN]; ++ u32 *mac_addr_l32; ++ u16 *mac_addr_h16; ++ ++ if (!peer) ++ return; ++ ++ /* mec_mac_addr has the swapped mac_addr after 4 bytes (sizeof(u32)) ++ * mec_mac_addr[0] ++ * | ++ * 03:0a:00:00:2d:15:22:f0:fd:8c ++ * ^ ++ * Swapped MAC address present after 4 bytes ++ * MAC address after swapping is 8c:fd:f0:22:15:2d */ ++ ++ mac_addr_l32 = (u32 *) (mec_mac_addr + sizeof(u32)); ++ mac_addr_h16 = (u16 *) (mec_mac_addr + sizeof(u32) + sizeof(u32)); ++ ++ *mac_addr_l32 = swab32(*mac_addr_l32); ++ *mac_addr_h16 = swab16(*mac_addr_h16); ++ ++ memcpy(mac_addr, mac_addr_h16, ETH_ALEN - 4); ++ memcpy(mac_addr + 2, mac_addr_l32, 4); ++ ++ if (!ether_addr_equal(ar->mac_addr, mac_addr)) { ++ spin_lock_bh(&ab->base_lock); ++ ath11k_peer_add_ast(ar, peer, mac_addr, ++ ATH11K_AST_TYPE_MEC); ++ spin_unlock_bh(&ab->base_lock); ++ } ++} ++ ++static void ath11k_nss_vdev_spl_receive_ext_wdsdata(struct ath11k_vif *arvif, ++ struct sk_buff *skb, ++ struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ enum wifi_vdev_ext_wds_info_type wds_type; ++ u8 is_sa_valid = 0, addr4_valid = 0; ++ u16 peer_id; ++ u8 src_mac[ETH_ALEN]; ++ ++ is_sa_valid = wds_metadata->is_sa_valid; ++ addr4_valid = wds_metadata->addr4_valid; ++ wds_type = wds_metadata->wds_type; ++ peer_id = wds_metadata->peer_id; ++ ++ memcpy(src_mac, ((struct ethhdr *)skb->data)->h_source, ETH_ALEN); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS,"receive_ext_wdsdata wds_type %d peer id %u sa_valid %d addr4_valid %d src_mac %pM\n", ++ wds_type, peer_id, is_sa_valid, addr4_valid, src_mac); ++ ++ switch (wds_type) { ++ case NSS_WIFI_VDEV_WDS_TYPE_RX: ++ ath11k_nss_wds_type_rx(ar, src_mac, is_sa_valid, ++ addr4_valid, peer_id); ++ break; ++ case NSS_WIFI_VDEV_WDS_TYPE_MEC: ++ ath11k_nss_mec_handler(ar, (u8 *)(skb->data)); ++ break; ++ default: ++ ath11k_warn(ab, "unsupported wds_type %d\n", wds_type); ++ break; ++ } ++} ++ ++static bool ath11k_nss_vdev_data_receive_mec_check(struct ath11k *ar, ++ struct sk_buff *skb) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_ast_entry *ast_entry = NULL; ++ u8 src_mac[ETH_ALEN]; ++ ++ memcpy(src_mac, ((struct ethhdr *)skb->data)->h_source, ETH_ALEN); ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "ath11k_nss_vdev_data_receive_mec_check src mac %pM\n", ++ src_mac); ++ ++ spin_lock_bh(&ab->base_lock); ++ ast_entry = ath11k_peer_ast_find_by_addr(ab, src_mac); ++ ++ if (ast_entry && ast_entry->type == ATH11K_AST_TYPE_MEC) { ++ spin_unlock_bh(&ab->base_lock); ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "dropping mec traffic from %pM\n", ast_entry->addr); ++ return true; ++ } ++ ++ spin_unlock_bh(&ab->base_lock); ++ return false; ++} ++ ++static int ath11k_nss_undecap(struct ath11k_vif *arvif, struct sk_buff *skb, ++ int *data_offs, bool *eth_decap) ++{ ++ enum ath11k_hw_txrx_mode decap_type; ++ ++ decap_type = arvif->nss.decap; ++ ++ switch (decap_type) { ++ case ATH11K_HW_TXRX_RAW: ++ return ath11k_nss_undecap_raw(arvif, skb, data_offs); ++ case ATH11K_HW_TXRX_NATIVE_WIFI: ++ return ath11k_nss_undecap_nwifi(arvif, skb, data_offs); ++ case ATH11K_HW_TXRX_ETHERNET: ++ *eth_decap = true; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static void ++ath11k_nss_vdev_special_data_receive(struct net_device *dev, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ struct nss_wifi_vdev_per_packet_metadata *wifi_metadata = NULL; ++ struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata = NULL; ++ struct wireless_dev *wdev; ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ bool eth_decap = false; ++ int data_offs = 0; ++ int ret = 0; ++ ++ if (!dev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ wdev = dev->ieee80211_ptr; ++ if (!wdev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ arvif = (struct ath11k_vif *)vif->drv_priv; ++ if (!arvif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ dma_unmap_single(ab->dev, virt_to_phys(skb->head), ++ NSS_WIFI_VDEV_PER_PACKET_METADATA_OFFSET + ++ sizeof(struct nss_wifi_vdev_per_packet_metadata), ++ DMA_FROM_DEVICE); ++ ++ wifi_metadata = (struct nss_wifi_vdev_per_packet_metadata *)(skb->head + ++ NSS_WIFI_VDEV_PER_PACKET_METADATA_OFFSET); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "dp special data from nss: wifi_metadata->pkt_type %d", ++ wifi_metadata->pkt_type); ++ ++ ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap); ++ if (ret) { ++ ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", ++ arvif->nss.decap, ret); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ if (eth_decap && wifi_metadata->pkt_type == ++ NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_WDS_LEARN) { ++ wds_metadata = &wifi_metadata->metadata.wds_metadata; ++ ath11k_nss_vdev_spl_receive_ext_wdsdata(arvif, skb, ++ wds_metadata); ++ } ++ ++ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++} ++ + static void + ath11k_nss_vdev_data_receive(struct net_device *dev, struct sk_buff *skb, + __attribute__((unused)) struct napi_struct *napi) + { +- enum ath11k_hw_txrx_mode decap_type; + struct wireless_dev *wdev = NULL; + struct ieee80211_vif *vif = NULL; + struct ath11k_vif *arvif; +@@ -591,28 +828,16 @@ ath11k_nss_vdev_data_receive(struct net_ + ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss: ", + skb->data, skb->len); + +- decap_type = arvif->nss.decap; +- +- switch (decap_type) { +- case ATH11K_HW_TXRX_RAW: +- ret = ath11k_nss_undecap_raw(arvif, skb, &data_offs); +- break; +- case ATH11K_HW_TXRX_NATIVE_WIFI: +- ret = ath11k_nss_undecap_nwifi(arvif, skb, &data_offs); +- break; +- case ATH11K_HW_TXRX_ETHERNET: +- /* no changes required for ethernet decap */ +- ret = 0; +- eth_decap = true; +- break; +- default: +- ret = -EINVAL; +- break; ++ if ((vif->type == NL80211_IFTYPE_STATION && wdev->use_4addr) && ++ ath11k_nss_vdev_data_receive_mec_check(arvif->ar, skb)) { ++ dev_kfree_skb_any(skb); ++ return; + } + ++ ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap); + if (ret) { +- ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", decap_type, +- ret); ++ ath11k_warn(ab, "error in nss rx undecap, type %d err %d\n", ++ arvif->nss.decap, ret); + dev_kfree_skb_any(skb); + return; + } +@@ -1321,7 +1546,7 @@ void ath11k_nss_update_sta_rxrate(struct + peer->nss.nss_stats->rxrate.bw = ath11k_mac_bw_to_mac80211_bw(ppdu_info->bw); + } + +-int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr) ++int ath11k_nss_peer_delete(struct ath11k_base *ab, u32 vdev_id, const u8 *addr) + { + struct nss_wifili_peer_msg *peer_msg; + struct nss_wifili_msg *wlmsg = NULL; +@@ -1335,9 +1560,10 @@ int ath11k_nss_peer_delete(struct ath11k + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_addr(ab, addr); ++ peer = ath11k_peer_find(ab, vdev_id, addr); + if (!peer) { +- ath11k_warn(ab, "peer (%pM) not found for nss peer delete\n", addr); ++ ath11k_warn(ab, "peer (%pM) not found on vdev_id %d for nss peer delete\n", ++ addr, vdev_id); + spin_unlock_bh(&ab->base_lock); + return -EINVAL; + } +@@ -1410,8 +1636,9 @@ free_peer: + return ret; + } + +-int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer) ++int ath11k_nss_peer_create(struct ath11k *ar, struct ath11k_peer *peer) + { ++ struct ath11k_base *ab = ar->ab; + struct nss_wifili_peer_msg *peer_msg; + struct nss_wifili_msg *wlmsg = NULL; + nss_wifili_msg_callback_t msg_cb; +@@ -1468,17 +1695,23 @@ int ath11k_nss_peer_create(struct ath11k + status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); + if (status != NSS_TX_SUCCESS) { + ret = -EINVAL; +- ath11k_warn(ab, "nss send peer (%pM) create msg tx error\n", +- peer->addr); ++ ath11k_warn(ab, "nss send peer (%pM) create msg tx error: %d\n", ++ peer->addr, status); + goto peer_mem_free; + } + +- ret = 0; + ath11k_dbg(ab, ATH11K_DBG_NSS, + "nss peer_create msg success mac:%pM vdev:%d peer_id:%d hw_ast_idx:%d ast_hash:%d\n", + peer_msg->peer_mac_addr, peer_msg->vdev_id, peer_msg->peer_id, + peer_msg->hw_ast_idx, peer_msg->tx_ast_hash); + ++ ret = ath11k_peer_add_ast(ar, peer, peer->addr, ++ ATH11K_AST_TYPE_STATIC); ++ if (ret) { ++ ath11k_warn(ab, "failed to add STATIC ast: %d\n", ret); ++ goto peer_mem_free; ++ } ++ + peer->nss.nss_stats = kzalloc(sizeof(*peer->nss.nss_stats), GFP_ATOMIC); + if (!peer->nss.nss_stats) { + ret = -ENOMEM; +@@ -1497,6 +1730,199 @@ msg_free: + return ret; + } + ++int ath11k_nss_add_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, enum ath11k_ast_entry_type type) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct nss_wifili_wds_peer_msg *wds_peer_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (wlmsg == NULL) ++ return -ENOMEM; ++ ++ wds_peer_msg = &wlmsg->msg.wdspeermsg; ++ ++ wds_peer_msg->pdev_id = ar->pdev->pdev_id; ++ wds_peer_msg->ast_type = type; ++ wds_peer_msg->peer_id = peer->peer_id; ++ ++ if (type == ATH11K_AST_TYPE_MEC) ++ ether_addr_copy(wds_peer_msg->peer_mac, ar->mac_addr); ++ else ++ ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); ++ ++ ether_addr_copy(wds_peer_msg->dest_mac, dest_mac); ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_WDS_PEER_ADD_MSG, ++ sizeof(struct nss_wifili_wds_peer_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ret = -EINVAL; ++ ath11k_warn(ab, "nss send wds add peer msg tx error: %d\n", ++ status); ++ goto msg_free; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss add wds peer success peer mac:%pM dest mac:%pM peer_id:%d\n", ++ wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); ++ ++msg_free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++int ath11k_nss_update_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct nss_wifili_wds_peer_msg *wds_peer_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (wlmsg == NULL) ++ return -ENOMEM; ++ ++ wds_peer_msg = &wlmsg->msg.wdspeermsg; ++ ++ wds_peer_msg->pdev_id = ar->pdev->pdev_id; ++ wds_peer_msg->ast_type = ATH11K_AST_TYPE_WDS; ++ wds_peer_msg->peer_id = peer->peer_id; ++ ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); ++ ether_addr_copy(wds_peer_msg->dest_mac, dest_mac); ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_WDS_PEER_UPDATE_MSG, ++ sizeof(struct nss_wifili_wds_peer_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ret = -EINVAL; ++ ath11k_warn(ab, "nss send wds update peer msg tx error: %d\n", ++ status); ++ goto msg_free; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss update wds peer success peer mac:%pM dest mac:%pM peer_id:%d\n", ++ wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); ++ ++msg_free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, enum ath11k_ast_entry_type type) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct nss_wifili_wds_peer_map_msg *wds_peer_map_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (wlmsg == NULL) ++ return -ENOMEM; ++ ++ wds_peer_map_msg = &wlmsg->msg.wdspeermapmsg; ++ ++ wds_peer_map_msg->vdev_id = peer->vdev_id; ++ wds_peer_map_msg->ast_idx = peer->hw_peer_id; ++ ++ if (type == ATH11K_AST_TYPE_MEC) ++ wds_peer_map_msg->peer_id = NSS_WIFILI_MEC_PEER_ID; ++ else ++ wds_peer_map_msg->peer_id = peer->peer_id; ++ ++ ether_addr_copy(wds_peer_map_msg->dest_mac, dest_mac); ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_WDS_PEER_MAP_MSG, ++ sizeof(struct nss_wifili_wds_peer_map_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ret = -EINVAL; ++ ath11k_warn(ab, "nss send wds peer map msg tx error: %d\n", ++ status); ++ goto msg_free; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss wds peer map success mac:%pM hw_ast_idx:%d peer_id:%d\n", ++ wds_peer_map_msg->dest_mac, wds_peer_map_msg->ast_idx, wds_peer_map_msg->peer_id); ++ ++msg_free: ++ kfree(wlmsg); ++ return ret; ++} ++ ++int ath11k_nss_del_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct nss_wifili_wds_peer_msg *wds_peer_msg; ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (wlmsg == NULL) ++ return -ENOMEM; ++ ++ wds_peer_msg = &wlmsg->msg.wdspeermsg; ++ ++ wds_peer_msg->pdev_id = ar->pdev->pdev_id; ++ wds_peer_msg->ast_type = ATH11K_AST_TYPE_NONE; ++ wds_peer_msg->peer_id = peer->peer_id; ++ ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); ++ ether_addr_copy(wds_peer_msg->dest_mac, dest_mac); ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_WDS_PEER_DEL_MSG, ++ sizeof(struct nss_wifili_wds_peer_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ret = -EINVAL; ++ ath11k_warn(ab, "nss send wds del peer msg tx error: %d\n", ++ status); ++ goto msg_free; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss del wds peer success peer mac:%pM dest mac:%pM peer_id:%d\n", ++ wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); ++ ++msg_free: ++ kfree(wlmsg); ++ return ret; ++} ++ + /*-------------------------------INIT/DEINIT---------------------------------*/ + + static int ath11k_nss_radio_buf_cfg(struct ath11k *ar, int range, int buf_sz) +@@ -1890,7 +2316,7 @@ static int ath11k_nss_init(struct ath11k + + status = nss_wifili_tx_msg(nss_contex, wlmsg); + if (status != NSS_TX_SUCCESS) { +- ath11k_warn(ab, "failure to send nss init msg\n"); ++ ath11k_warn(ab, "failure to send nss init msg: %d \n", status); + goto unregister; + } + +@@ -1944,7 +2370,8 @@ static int ath11k_nss_stats_cfg(struct a + + status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); + if (status != NSS_TX_SUCCESS) { +- ath11k_warn(ab, "nss stats cfg %d msg tx failure\n", nss_msg); ++ ath11k_warn(ab, "nss stats cfg %d msg tx failure: %d\n", ++ nss_msg, status); + ret = -EINVAL; + goto free; + } +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -17,12 +17,14 @@ struct ath11k_base; + struct ath11k_vif; + struct ath11k_peer; + struct ath11k_sta; ++enum ath11k_ast_entry_type; + struct hal_rx_mon_ppdu_info; + struct hal_rx_user_status; + + /* NSS DBG macro is not included as part of debug enum to avoid + * frequent changes during upgrade*/ +-#define ATH11K_DBG_NSS 0x80000000 ++#define ATH11K_DBG_NSS 0x40000000 ++#define ATH11K_DBG_NSS_WDS 0x80000000 + + /* WIFILI Supported Target Types */ + #define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN 0xFF +@@ -205,11 +207,19 @@ int ath11k_nss_vdev_create(struct ath11k + void ath11k_nss_vdev_delete(struct ath11k_vif *arvif); + int ath11k_nss_vdev_up(struct ath11k_vif *arvif); + int ath11k_nss_vdev_down(struct ath11k_vif *arvif); +-int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr); ++int ath11k_nss_peer_delete(struct ath11k_base *ab, u32 vdev_id, const u8 *addr); + int ath11k_nss_set_peer_authorize(struct ath11k *ar, u16 peer_id); +-int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer); ++int ath11k_nss_peer_create(struct ath11k *ar, struct ath11k_peer *peer); + void ath11k_nss_peer_stats_enable(struct ath11k *ar); + void ath11k_nss_peer_stats_disable(struct ath11k *ar); ++int ath11k_nss_add_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, enum ath11k_ast_entry_type type); ++int ath11k_nss_update_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac); ++int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, enum ath11k_ast_entry_type type); ++int ath11k_nss_del_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac); + int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer, + struct ieee80211_key_conf *key_conf); + void ath11k_nss_update_sta_stats(struct station_info *sinfo, +@@ -271,12 +281,37 @@ static inline int ath11k_nss_vdev_down(s + return 0; + } + +-static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, const u8 *addr) ++static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, u32 vdev_id, ++ const u8 *addr) + { + return 0; + } + +-static inline int ath11k_nss_peer_create(struct ath11k_base *ab, struct ath11k_peer *peer) ++static inline int ath11k_nss_add_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, int type) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_update_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++ u8 *dest_mac, int type) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_del_wds_peer(struct ath11k_vif *arvif, struct ath11k_peer *peer, ++ u8 *dest_mac) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_peer_create(struct ath11k *ar, struct ath11k_peer *peer) + { + return 0; + } diff --git a/package/kernel/mac80211/patches/nss/ath11k/211-002-ath11k-add-WDS-offload-support-on-NSS-offload-for-STA-mode.patch b/package/kernel/mac80211/patches/nss/ath11k/211-002-ath11k-add-WDS-offload-support-on-NSS-offload-for-STA-mode.patch new file mode 100644 index 00000000000000..8ad2cc71f8981d --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/211-002-ath11k-add-WDS-offload-support-on-NSS-offload-for-STA-mode.patch @@ -0,0 +1,973 @@ +From e6ecf9e1cc115b5821a880c6dccc5d8c9cd76fbd Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Tue, 15 Sep 2020 21:12:37 +0530 +Subject: [PATCH] ath11k: add WDS offload support on NSS offload for STA mode + +When 4addr is set ON for STA interface along with NSS enabled case, WDS +offload is enabled for ethernet mode. + +STA mode uses MEC (Multicast Echo Check) AST (Address Search Table) entry +to update the bridge MAC address from NSS to drop the locally generated +multicast/broadcast frames as multicast echo frames. + +AST handling and callbacks to NSS WDS APIs are added. + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/core.h | 8 +- + drivers/net/wireless/ath/ath11k/dp.h | 6 +- + drivers/net/wireless/ath/ath11k/dp_rx.c | 18 +- + drivers/net/wireless/ath/ath11k/dp_tx.c | 2 +- + drivers/net/wireless/ath/ath11k/peer.c | 389 +++++++++++++++++++++++++++++++- + drivers/net/wireless/ath/ath11k/peer.h | 117 ++++++++++ + drivers/net/wireless/ath/ath11k/wmi.c | 99 +++++++- + drivers/net/wireless/ath/ath11k/wmi.h | 33 +++ + 8 files changed, 658 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -642,6 +642,7 @@ struct ath11k { + struct ath11k_pdev_wmi *wmi; + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + struct ath11k_nss nss; ++ struct ath11k_peer *bss_peer; + #endif + struct ath11k_pdev_dp dp; + u8 mac_addr[ETH_ALEN]; +@@ -1047,6 +1048,9 @@ struct ath11k_base { + } testmode; + #endif + ++ u32 max_ast_index; ++ u32 num_ast_entries; ++ + /* must be last */ + u8 drv_priv[] __aligned(sizeof(void *)); + }; +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -1105,13 +1105,16 @@ struct htt_t2h_peer_map_event { + #define HTT_T2H_PEER_UNMAP_INFO_PEER_ID HTT_T2H_PEER_MAP_INFO_PEER_ID + #define HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16 \ + HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 +-#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M +-#define HTT_T2H_PEER_MAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S ++#define HTT_T2H_PEER_UNMAP_INFO1_NEXT_HOP_M HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M ++#define HTT_T2H_PEER_UNMAP_INFO1_NEXT_HOP_S HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S ++#define HTT_T2H_PEER_UNMAP_INFO3_WDS_FREE_COUNT GENMASK(15, 0) + + struct htt_t2h_peer_unmap_event { + u32 info; + u32 mac_addr_l32; + u32 info1; ++ u32 info2; ++ u32 info3; + } __packed; + + struct htt_resp_msg { +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1857,6 +1857,8 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + u16 peer_mac_h16; + u16 ast_hash; + u16 hw_peer_id; ++ u32 free_wds_count; ++ bool is_wds = false; + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "dp_htt rx msg type :0x%0x\n", type); + +@@ -1892,15 +1894,29 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + resp->peer_map_ev.info2); + hw_peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID, + resp->peer_map_ev.info1); +- ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash, +- hw_peer_id); ++ is_wds = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M, ++ resp->peer_map_ev.info2); ++ ath11k_peer_map_v2_event(ab, vdev_id, peer_id, mac_addr, ast_hash, ++ hw_peer_id, is_wds); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: +- case HTT_T2H_MSG_TYPE_PEER_UNMAP2: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); + ath11k_peer_unmap_event(ab, peer_id); + break; ++ case HTT_T2H_MSG_TYPE_PEER_UNMAP2: ++ peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, ++ resp->peer_unmap_ev.info); ++ peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO1_MAC_ADDR_H16, ++ resp->peer_unmap_ev.info1); ++ ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, ++ peer_mac_h16, mac_addr); ++ is_wds = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO1_NEXT_HOP_M, ++ resp->peer_unmap_ev.info1); ++ free_wds_count = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO3_WDS_FREE_COUNT, ++ resp->peer_unmap_ev.info3); ++ ath11k_peer_unmap_v2_event(ab, peer_id, mac_addr, is_wds, free_wds_count); ++ break; + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath11k_htt_pull_ppdu_stats(ab, skb); + break; +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -495,7 +495,7 @@ ath11k_dp_tx_process_htt_tx_complete(str + break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: + /* This event is to be handled only when the driver decides to +- * use WDS offload functionality. ++ * use WDS offload functionality on NSS disabled case. + */ + break; + default: +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -108,6 +108,287 @@ struct ath11k_peer *ath11k_peer_find_by_ + return NULL; + } + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k_base *ab, ++ struct ath11k_peer *peer, ++ u8* addr) ++{ ++ struct ath11k_ast_entry *ast_entry; ++ ++ lockdep_assert_held(&ab->base_lock); ++ ++ list_for_each_entry(ast_entry, &peer->ast_entry_list, ase_list) ++ if (ether_addr_equal(ast_entry->addr, addr)) ++ return ast_entry; ++ ++ return NULL; ++} ++ ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++ u8* addr) ++{ ++ struct ath11k_ast_entry *ast_entry; ++ struct ath11k_peer *peer; ++ ++ lockdep_assert_held(&ab->base_lock); ++ ++ list_for_each_entry(peer, &ab->peers, list) ++ list_for_each_entry(ast_entry, &peer->ast_entry_list, ase_list) ++ if (ether_addr_equal(ast_entry->addr, addr)) ++ return ast_entry; ++ ++ return NULL; ++} ++ ++void ath11k_peer_ast_wds_wmi_wk(struct work_struct *wk) ++{ ++ struct ath11k_ast_entry *ast_entry = container_of(wk, ++ struct ath11k_ast_entry, ++ wds_wmi_wk); ++ struct ath11k *ar; ++ struct ath11k_peer *peer; ++ int ret; ++ ++ if (!ast_entry) ++ return; ++ ++ ar = ast_entry->ar; ++ peer = ast_entry->peer; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "ath11k_peer_ast_wds_wmi_wk action %d ast_entry %pM next_node %pM vdev %d\n", ++ ast_entry->action, ast_entry->addr, ast_entry->next_node_mac, ++ ast_entry->vdev_id); ++ ++ if (ast_entry->action == ATH11K_WDS_WMI_ADD) { ++ ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, ++ ast_entry->next_node_mac, ++ ast_entry->addr, ++ ast_entry->vdev_id, ++ true); ++ if (ret) { ++ ath11k_warn(ar->ab, "add wds_entry_cmd failed %d for %pM next_node %pM\n", ++ ret, ast_entry->addr, ++ ast_entry->next_node_mac); ++ if (peer) ++ ath11k_nss_del_wds_peer(ar, peer, ++ ast_entry->addr); ++ } ++ } else if (ast_entry->action == ATH11K_WDS_WMI_UPDATE) { ++ if (!peer) ++ return; ++ ++ ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, peer->addr, ++ ast_entry->addr, ++ ast_entry->vdev_id, ++ false); ++ if (ret) ++ ath11k_warn(ar->ab, "update wds_entry_cmd failed %d for %pM on peer %pM\n", ++ ret, ast_entry->addr, peer->addr); ++ } ++} ++ ++int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, enum ath11k_ast_entry_type type) ++{ ++ struct ath11k_ast_entry *ast_entry = NULL; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (ab->num_ast_entries == ab->max_ast_index) { ++ ath11k_warn(ab, "failed to add ast for %pM due to insufficient ast entry resource %d in target\n", ++ mac_addr, ab->max_ast_index); ++ return -ENOBUFS; ++ } ++ ++ if (type != ATH11K_AST_TYPE_STATIC) { ++ ast_entry = ath11k_peer_ast_find_by_addr(ab, mac_addr); ++ if (ast_entry) { ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ast_entry %pM already present on peer %pM\n", ++ mac_addr, ast_entry->peer->addr); ++ return 0; ++ } ++ } ++ ++ ast_entry = kzalloc(sizeof(*ast_entry), GFP_ATOMIC); ++ if (!ast_entry) { ++ ath11k_warn(ab, "failed to alloc ast_entry for %pM\n", ++ mac_addr); ++ return -ENOMEM; ++ } ++ ++ switch (type) { ++ case ATH11K_AST_TYPE_STATIC: ++ peer->self_ast_entry = ast_entry; ++ ast_entry->type = ATH11K_AST_TYPE_STATIC; ++ break; ++ case ATH11K_AST_TYPE_SELF: ++ peer->self_ast_entry = ast_entry; ++ ast_entry->type = ATH11K_AST_TYPE_SELF; ++ break; ++ case ATH11K_AST_TYPE_WDS: ++ ast_entry->type = ATH11K_AST_TYPE_WDS; ++ ast_entry->next_hop = 1; ++ break; ++ case ATH11K_AST_TYPE_MEC: ++ ast_entry->type = ATH11K_AST_TYPE_MEC; ++ ast_entry->next_hop = 1; ++ break; ++ default: ++ ath11k_warn(ab, "unsupported ast_type %d", type); ++ kfree(ast_entry); ++ return -EINVAL; ++ } ++ ++ INIT_LIST_HEAD(&ast_entry->ase_list); ++ INIT_WORK(&ast_entry->wds_wmi_wk, ath11k_peer_ast_wds_wmi_wk); ++ ast_entry->vdev_id = peer->vdev_id; ++ ast_entry->pdev_idx = peer->pdev_idx; ++ ast_entry->is_mapped = false; ++ ast_entry->is_active = true; ++ ast_entry->peer = peer; ++ ast_entry->ar = ar; ++ ether_addr_copy(ast_entry->addr, mac_addr); ++ ++ list_add_tail(&ast_entry->ase_list, &peer->ast_entry_list); ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_add_ast peer %pM ast_entry %pM, ast_type %d\n", ++ peer->addr, mac_addr, ast_entry->type); ++ ++ if (type == ATH11K_AST_TYPE_MEC) ++ ether_addr_copy(ast_entry->next_node_mac, ar->mac_addr); ++ else if (type == ATH11K_AST_TYPE_WDS) ++ ether_addr_copy(ast_entry->next_node_mac, peer->addr); ++ ++ if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || ++ (ast_entry->type == ATH11K_AST_TYPE_MEC)) { ++ ath11k_nss_add_wds_peer(ar, peer, mac_addr, ast_entry->type); ++ ast_entry->action = ATH11K_WDS_WMI_ADD; ++ ieee80211_queue_work(ar->hw, &ast_entry->wds_wmi_wk); ++ } ++ ++ ab->num_ast_entries++; ++ return 0; ++} ++ ++int ath11k_peer_update_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ struct ath11k_ast_entry *ast_entry) ++{ ++ struct ath11k_peer *old_peer = ast_entry->peer; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (!ast_entry->is_mapped) { ++ ath11k_warn(ab, "ath11k_peer_update_ast: ast_entry %pM not mapped yet\n", ++ ast_entry->addr); ++ return -EINVAL; ++ } ++ ++ if (ether_addr_equal(old_peer->addr, peer->addr) && ++ (ast_entry->type == ATH11K_AST_TYPE_WDS) && ++ (ast_entry->vdev_id == peer->vdev_id) && ++ (ast_entry->is_active)) ++ return 0; ++ ++ ast_entry->vdev_id = peer->vdev_id; ++ ast_entry->pdev_idx = peer->pdev_idx; ++ ast_entry->type = ATH11K_AST_TYPE_WDS; ++ ast_entry->is_active = true; ++ ast_entry->peer = peer; ++ ++ list_move_tail(&ast_entry->ase_list, &peer->ast_entry_list); ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_update_ast old peer %pM new peer %pM ast_entry %pM\n", ++ old_peer->addr, peer->addr, ast_entry->addr); ++ ++ flush_work(&ast_entry->wds_wmi_wk); ++ ast_entry->action = ATH11K_WDS_WMI_UPDATE; ++ ieee80211_queue_work(ar->hw, &ast_entry->wds_wmi_wk); ++ ++ return 0; ++} ++ ++void ath11k_peer_map_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, u16 hw_peer_id, u16 ast_hash) ++{ ++ struct ath11k_ast_entry *ast_entry = NULL; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (!peer) ++ return; ++ ++ ast_entry = ath11k_peer_ast_find_by_peer(ab, peer, mac_addr); ++ ++ if (ast_entry) { ++ ast_entry->ast_idx = hw_peer_id; ++ ast_entry->is_active = true; ++ ast_entry->is_mapped = true; ++ ast_entry->ast_hash_value = ast_hash; ++ ++ if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || ++ (ast_entry->type == ATH11K_AST_TYPE_MEC)) ++ ath11k_nss_map_wds_peer(ar, peer, mac_addr, ++ ast_entry->type); ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_map_ast peer %pM ast_entry %pM\n", ++ peer->addr, ast_entry->addr); ++ } ++ ++} ++ ++void ath11k_peer_del_ast(struct ath11k *ar, struct ath11k_ast_entry *ast_entry) ++{ ++ struct ath11k_peer *peer; ++ struct ath11k_base *ab = ar->ab; ++ ++ if (!ast_entry || !ast_entry->peer) ++ return; ++ ++ peer = ast_entry->peer; ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_del_ast peer %pM ast_entry %pM\n", ++ peer->addr, ast_entry->addr); ++ ++ if (ast_entry->is_mapped) ++ list_del(&ast_entry->ase_list); ++ ++ /* WDS, MEC type AST entries need to be deleted on NSS */ ++ if (ast_entry->next_hop) ++ ath11k_nss_del_wds_peer(ar, peer, ast_entry->addr); ++ ++ cancel_work_sync(&ast_entry->wds_wmi_wk); ++ kfree(ast_entry); ++ ++ ab->num_ast_entries--; ++} ++ ++void ath11k_peer_ast_cleanup(struct ath11k *ar, struct ath11k_peer *peer, ++ bool is_wds, u32 free_wds_count) ++{ ++ struct ath11k_ast_entry *ast_entry, *tmp; ++ u32 ast_deleted_count = 0; ++ ++ if (peer->self_ast_entry) { ++ ath11k_peer_del_ast(ar, peer->self_ast_entry); ++ peer->self_ast_entry = NULL; ++ } ++ ++ list_for_each_entry_safe(ast_entry, tmp, &peer->ast_entry_list, ++ ase_list) { ++ if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || ++ (ast_entry->type == ATH11K_AST_TYPE_MEC)) ++ ast_deleted_count++; ++ ath11k_peer_del_ast(ar, ast_entry); ++ } ++ ++ if (!is_wds) { ++ if (ast_deleted_count != free_wds_count) ++ ath11k_warn(ar->ab, "ast_deleted_count (%d) mismatch on peer %pM free_wds_count (%d)!\n", ++ ast_deleted_count, peer->addr, free_wds_count); ++ else ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "ast_deleted_count (%d) on peer %pM free_wds_count (%d)\n", ++ ast_deleted_count, peer->addr, free_wds_count); ++ } ++} ++#endif ++ + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) + { + struct ath11k_peer *peer; +@@ -132,11 +413,67 @@ exit: + spin_unlock_bh(&ab->base_lock); + } + ++void ath11k_peer_unmap_v2_event(struct ath11k_base *ab, u16 peer_id, u8 *mac_addr, ++ bool is_wds, u32 free_wds_count) ++{ ++ struct ath11k_peer *peer; ++ struct ath11k *ar; ++ ++ spin_lock_bh(&ab->base_lock); ++ ++ peer = ath11k_peer_find_list_by_id(ab, peer_id); ++ if (!peer) { ++ ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", ++ peer_id); ++ goto exit; ++ } ++ ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); ++ if (!ar) { ++ ath11k_warn(ab, "peer-unmap-event: unknown peer vdev id %d\n", ++ peer->vdev_id); ++ goto free_peer; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d is_wds %d free_wds_count %d\n", ++ peer->vdev_id, peer->addr, peer_id, is_wds, free_wds_count); ++ ++ if (ab->nss.enabled) { ++ if (is_wds) { ++ struct ath11k_ast_entry *ast_entry = ++ ath11k_peer_ast_find_by_peer(ab, peer, mac_addr); ++ ++ if (ast_entry) ++ ath11k_peer_del_ast(ar, ast_entry); ++ rcu_read_unlock(); ++ goto exit; ++ } else ++ ath11k_peer_ast_cleanup(ar, peer, is_wds, free_wds_count); ++ } ++ ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ if (ar->bss_peer && ether_addr_equal(ar->bss_peer->addr, peer->addr)) ++ ar->bss_peer = NULL; ++#endif ++free_peer: ++ rcu_read_unlock(); ++ list_del(&peer->list); ++ kfree(peer); ++ wake_up(&ab->peer_mapping_wq); ++ ++exit: ++ spin_unlock_bh(&ab->base_lock); ++} ++ + void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash, u16 hw_peer_id) + { + struct ath11k_peer *peer; ++ struct ath11k *ar = NULL; + ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, vdev_id, mac_addr); + if (!peer) { +@@ -151,8 +488,8 @@ void ath11k_peer_map_event(struct ath11k + ether_addr_copy(peer->addr, mac_addr); + list_add(&peer->list, &ab->peers); + wake_up(&ab->peer_mapping_wq); +- if (ab->nss.enabled) +- ath11k_nss_peer_create(ab, peer); ++ if (ab->nss.enabled && ar) ++ ath11k_nss_peer_create(ar, peer); + } + + ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer map vdev %d peer %pM id %d\n", +@@ -160,6 +497,69 @@ void ath11k_peer_map_event(struct ath11k + + exit: + spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++} ++ ++void ath11k_peer_map_v2_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, ++ u8 *mac_addr, u16 ast_hash, u16 hw_peer_id, ++ bool is_wds) ++{ ++ struct ath11k_peer *peer; ++ struct ath11k *ar = NULL; ++ int ret; ++ ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find(ab, vdev_id, mac_addr); ++ if (!peer && !is_wds) { ++ peer = kzalloc(sizeof(*peer), GFP_ATOMIC); ++ if (!peer) { ++ ath11k_warn(ab, "failed to allocated peer for %pM vdev_id %d\n", ++ mac_addr, vdev_id); ++ spin_unlock_bh(&ab->base_lock); ++ goto exit; ++ } ++ ++ peer->vdev_id = vdev_id; ++ peer->peer_id = peer_id; ++ peer->ast_hash = ast_hash; ++ peer->hw_peer_id = hw_peer_id; ++ ether_addr_copy(peer->addr, mac_addr); ++ list_add(&peer->list, &ab->peers); ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ INIT_LIST_HEAD(&peer->ast_entry_list); ++#endif ++ if (ab->nss.enabled && ar) { ++ ret = ath11k_nss_peer_create(ar, peer); ++ if (ret) { ++ ath11k_warn(ab, "failed to do nss peer create: %d\n", ++ ret); ++ goto peer_free; ++ } ++ } ++ wake_up(&ab->peer_mapping_wq); ++ } ++ ++ if (is_wds) ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ ++ if (ab->nss.enabled && ar) ++ ath11k_peer_map_ast(ar, peer, mac_addr, hw_peer_id, ast_hash); ++ ++ ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "htt peer map vdev %d peer %pM id %d is_wds %d\n", ++ vdev_id, mac_addr, peer_id, is_wds); ++ ++ spin_unlock_bh(&ab->base_lock); ++ goto exit; ++ ++peer_free: ++ spin_unlock_bh(&ab->base_lock); ++ mutex_lock(&ar->conf_mutex); ++ ath11k_peer_delete(ar, vdev_id, mac_addr); ++ mutex_unlock(&ar->conf_mutex); ++exit: ++ rcu_read_unlock(); + } + + static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, +@@ -256,20 +656,34 @@ err_clean: + + void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) + { +- struct ath11k_peer *peer, *tmp; ++ struct ath11k_peer *peer, *tmp_peer; + struct ath11k_base *ab = ar->ab; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct ath11k_ast_entry *ast_entry, *tmp_ast; ++#endif + + lockdep_assert_held(&ar->conf_mutex); + + mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); +- list_for_each_entry_safe(peer, tmp, &ab->peers, list) { ++ list_for_each_entry_safe(peer, tmp_peer, &ab->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + + ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ if (peer->self_ast_entry) { ++ ath11k_peer_del_ast(ar, peer->self_ast_entry); ++ peer->self_ast_entry = NULL; ++ } ++ ++ list_for_each_entry_safe(ast_entry, tmp_ast, ++ &peer->ast_entry_list, ase_list) ++ ath11k_peer_del_ast(ar, ast_entry); ++#endif ++ + ath11k_peer_rhash_delete(ab, peer); + list_del(&peer->list); + kfree(peer); +@@ -316,7 +730,7 @@ static int __ath11k_peer_delete(struct a + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->peer_delete_done); +- ath11k_nss_peer_delete(ar->ab, addr); ++ ath11k_nss_peer_delete(ar->ab, vdev_id, addr); + + mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); +@@ -391,6 +805,7 @@ int ath11k_peer_create(struct ath11k *ar + struct ieee80211_sta *sta, struct peer_create_params *param) + { + struct ath11k_peer *peer; ++ struct ieee80211_vif *vif = arvif->vif; + struct ath11k_sta *arsta; + int ret, fbret; + +@@ -466,6 +881,13 @@ int ath11k_peer_create(struct ath11k *ar + peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; + peer->vif = arvif->vif; + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ if (vif->type == NL80211_IFTYPE_STATION && ar->ab->nss.enabled) ++ ar->bss_peer = peer; ++ else ++ ar->bss_peer = NULL; ++#endif ++ + if (sta) { + arsta = ath11k_sta_to_arsta(sta); + arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) | +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -18,6 +18,47 @@ struct ppdu_user_delayba { + u32 resp_rate_flags; + }; + ++enum ath11k_ast_entry_type { ++ ATH11K_AST_TYPE_NONE, /* static ast entry for connected peer */ ++ ATH11K_AST_TYPE_STATIC, /* static ast entry for connected peer */ ++ ATH11K_AST_TYPE_SELF, /* static ast entry for self peer (STA mode) */ ++ ATH11K_AST_TYPE_WDS, /* WDS peer ast entry type*/ ++ ATH11K_AST_TYPE_MEC, /* Multicast echo ast entry type */ ++ ATH11K_AST_TYPE_WDS_HM, /* HM WDS entry */ ++ ATH11K_AST_TYPE_STA_BSS, /* BSS entry(STA mode) */ ++ ATH11K_AST_TYPE_DA, /* AST entry based on Destination address */ ++ ATH11K_AST_TYPE_WDS_HM_SEC, /* HM WDS entry for secondary radio */ ++ ATH11K_AST_TYPE_MAX ++}; ++ ++enum ath11k_wds_wmi_action { ++ ATH11K_WDS_WMI_ADD = 1, ++ ATH11K_WDS_WMI_UPDATE, ++ ++ ATH11K_WDS_WMI_MAX ++}; ++ ++struct ath11k_ast_entry { ++ u16 ast_idx; ++ u8 addr[ETH_ALEN]; ++ u8 next_node_mac[ETH_ALEN]; ++ enum ath11k_wds_wmi_action action; ++ struct work_struct wds_wmi_wk; ++ struct ath11k_peer *peer; ++ struct ath11k *ar; ++ bool next_hop; ++ bool is_active; ++ bool is_mapped; ++ u8 pdev_idx; ++ u8 vdev_id; ++ u16 ast_hash_value; ++ int ref_cnt; ++ enum ath11k_ast_entry_type type; ++ bool delete_in_progress; ++ void *cookie; ++ struct list_head ase_list; ++}; ++ + struct ath11k_peer { + struct list_head list; + struct ieee80211_sta *sta; +@@ -29,6 +70,10 @@ struct ath11k_peer { + u8 pdev_idx; + u16 hw_peer_id; + struct ath11k_nss_peer nss; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct ath11k_ast_entry *self_ast_entry; ++ struct list_head ast_entry_list; ++#endif + + /* protected by ab->data_lock */ + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; +@@ -54,8 +99,13 @@ struct ath11k_peer { + }; + + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); ++void ath11k_peer_unmap_v2_event(struct ath11k_base *ab, u16 peer_id, u8 *mac_addr, ++ bool is_wds, u32 free_wds_count); + void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash, u16 hw_peer_id); ++void ath11k_peer_map_v2_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id, ++ u8 *mac_addr, u16 ast_hash, u16 hw_peer_id, ++ bool is_wds); + struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, + const u8 *addr); + struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, +@@ -73,4 +123,71 @@ struct ath11k_peer *ath11k_peer_find_by_ + int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab); + void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab); + int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer); ++ ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++ u8* addr); ++int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, enum ath11k_ast_entry_type type); ++int ath11k_peer_update_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ struct ath11k_ast_entry *ast_entry); ++void ath11k_peer_map_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, u16 hw_peer_id, u16 ast_hash); ++void ath11k_peer_del_ast(struct ath11k *ar, struct ath11k_ast_entry *ast_entry); ++void ath11k_peer_ast_cleanup(struct ath11k *ar, struct ath11k_peer *peer, ++ bool is_wds, u32 free_wds_count); ++void ath11k_peer_ast_wds_wmi_wk(struct work_struct *wk); ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k_base *ab, ++ struct ath11k_peer *peer, ++ u8* addr); ++#else ++static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++ u8* addr) ++{ ++ return NULL; ++} ++ ++static inline int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, enum ath11k_ast_entry_type type) ++{ ++ return 0; ++} ++ ++static inline int ath11k_peer_update_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ struct ath11k_ast_entry *ast_entry) ++{ ++ return 0; ++} ++ ++static inline void ath11k_peer_map_ast(struct ath11k *ar, struct ath11k_peer *peer, ++ u8* mac_addr, u16 hw_peer_id, u16 ast_hash) ++{ ++ return; ++} ++ ++static inline void ath11k_peer_del_ast(struct ath11k *ar, ++ struct ath11k_ast_entry *ast_entry) ++{ ++ return; ++} ++ ++static inline void ath11k_peer_ast_cleanup(struct ath11k *ar, ++ struct ath11k_peer *peer, ++ bool is_wds, u32 free_wds_count) ++{ ++ return; ++} ++ ++static inline void ath11k_peer_ast_wds_wmi_wk(struct work_struct *wk) ++{ ++ return; ++} ++ ++static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k_base *ab, ++ struct ath11k_peer *peer, ++ u8* addr) ++{ ++ return NULL; ++} ++#endif /* CPTCFG_ATH11K_NSS_SUPPORT */ + #endif /* _PEER_H_ */ +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -156,6 +156,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, + [WMI_TAG_TWT_ADD_DIALOG_COMPLETE_EVENT] = { + .min_len = sizeof(struct wmi_twt_add_dialog_event) }, ++ [WMI_TAG_WDS_ADDR_EVENT] = { ++ .min_len = sizeof(struct wmi_wds_addr_event) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -1126,6 +1128,51 @@ int ath11k_wmi_send_peer_delete_cmd(stru + return ret; + } + ++int ath11k_wmi_send_add_update_wds_entry_cmd(struct ath11k *ar, ++ const u8 *peer_addr, ++ const u8 *wds_addr, u8 vdev_id, ++ bool add_wds) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_add_wds_entry_cmd *cmd; ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_add_wds_entry_cmd *)skb->data; ++ if (add_wds) ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_ADD_WDS_ENTRY_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ else ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PEER_UPDATE_WDS_ENTRY_CMD) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ ++ ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); ++ ether_addr_copy(cmd->wds_macaddr.addr, wds_addr); ++ cmd->vdev_id = vdev_id; ++ cmd->flags = WMI_WDS_FLAG_STATIC; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "WMI add WDS entry vdev_id %d peer_addr %pM, wds_addr %pM flags %x\n", ++ vdev_id, peer_addr, wds_addr, cmd->flags); ++ ++ if (add_wds) ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_ADD_WDS_ENTRY_CMDID); ++ else ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PEER_UPDATE_WDS_ENTRY_CMDID); ++ ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send WMI_PEER_%s_WDS_ENTRY cmd\n", ++ add_wds ? "ADD" : "UPDATE"); ++ dev_kfree_skb(skb); ++ } ++ ++ return ret; ++} ++ + int ath11k_wmi_send_pdev_set_regdomain(struct ath11k *ar, + struct pdev_set_regdomain_params *param) + { +@@ -6363,6 +6410,36 @@ static int ath11k_pull_peer_assoc_conf_e + return 0; + } + ++static int ath11k_pull_wds_addr_ev(struct ath11k_base *ab, struct sk_buff *skb, ++ struct wmi_wds_addr_arg *wds_addr_arg) ++{ ++ const void **tb; ++ const struct wmi_wds_addr_event *ev; ++ int ret; ++ ++ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); ++ if (IS_ERR(tb)) { ++ ret = PTR_ERR(tb); ++ ath11k_warn(ab, "failed to parse tlv: %d\n", ret); ++ return ret; ++ } ++ ++ ev = tb[WMI_TAG_WDS_ADDR_EVENT]; ++ if (!ev) { ++ ath11k_warn(ab, "failed to fetch wds peer ev"); ++ kfree(tb); ++ return -EPROTO; ++ } ++ ++ memcpy(wds_addr_arg->event_type, ev->event_type, WMI_NUM_WDS_EVENTS); ++ wds_addr_arg->vdev_id = ev->vdev_id; ++ wds_addr_arg->peer_macaddr = ev->peer_macaddr.addr; ++ wds_addr_arg->dst_macaddr = ev->dst_macaddr.addr; ++ ++ kfree(tb); ++ return 0; ++} ++ + static void ath11k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src, + struct ath11k_fw_stats_pdev *dst) + { +@@ -7278,6 +7355,7 @@ static int ath11k_wmi_tlv_rdy_parse(stru + + ether_addr_copy(ab->mac_addr, + fixed_param.ready_event_min.mac_addr.addr); ++ ab->max_ast_index = fixed_param.max_ast_index + 1; + ab->pktlog_defs_checksum = fixed_param.pktlog_defs_checksum; + break; + case WMI_TAG_ARRAY_FIXED_STRUCT: +@@ -8797,6 +8875,22 @@ exit: + kfree(tb); + } + ++static void ath11k_wmi_wds_peer_event(struct ath11k_base *ab, ++ struct sk_buff *skb) ++{ ++ struct wmi_wds_addr_arg wds_addr_arg = {0}; ++ ++ if (ath11k_pull_wds_addr_ev(ab, skb, &wds_addr_arg) != 0) { ++ ath11k_warn(ab, "failed to extract wds addr event"); ++ return; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "wds addr event vdev id %d peer macaddr %pM dst macaddr %pM\n", ++ wds_addr_arg.vdev_id, wds_addr_arg.peer_macaddr, ++ wds_addr_arg.dst_macaddr); ++} ++ + static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) + { + struct wmi_cmd_hdr *cmd_hdr; +@@ -8927,6 +9021,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath11k_wmi_gtk_offload_status_event(ab, skb); + break; ++ case WMI_WDS_PEER_EVENTID: ++ ath11k_wmi_wds_peer_event(ab, skb); ++ break; + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "unsupported event id 0x%x\n", id); + break; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3011,6 +3011,21 @@ struct wmi_peer_delete_cmd { + struct wmi_mac_addr peer_macaddr; + } __packed; + ++#define WMI_WDS_FLAG_STATIC 0x1 /* Disable aging & learning */ ++struct wmi_add_wds_entry_cmd { ++ u32 tlv_header; ++ struct wmi_mac_addr peer_macaddr; ++ struct wmi_mac_addr wds_macaddr; ++ u32 flags; ++ u32 vdev_id; ++} __packed; ++ ++struct wmi_remove_wds_entry_cmd { ++ u32 tlv_header; ++ struct wmi_mac_addr wds_macaddr; ++ u32 vdev_id; ++} __packed; ++ + struct wmi_peer_reorder_queue_setup_cmd { + u32 tlv_header; + u32 vdev_id; +@@ -4613,6 +4628,21 @@ struct wmi_probe_resp_tx_status_event { + u32 tx_status; + } __packed; + ++#define WMI_NUM_WDS_EVENTS 4 ++struct wmi_wds_addr_arg { ++ u32 event_type[WMI_NUM_WDS_EVENTS]; ++ const u8 *peer_macaddr; ++ const u8 *dst_macaddr; ++ u32 vdev_id; ++}; ++ ++struct wmi_wds_addr_event { ++ u32 event_type[WMI_NUM_WDS_EVENTS]; ++ struct wmi_mac_addr peer_macaddr; ++ struct wmi_mac_addr dst_macaddr; ++ u32 vdev_id; ++} __packed; ++ + /* + * PDEV statistics + */ +@@ -6412,6 +6442,9 @@ int ath11k_wmi_set_sta_ps_param(struct a + int ath11k_wmi_force_fw_hang_cmd(struct ath11k *ar, u32 type, u32 delay_time_ms); + int ath11k_wmi_send_peer_delete_cmd(struct ath11k *ar, + const u8 *peer_addr, u8 vdev_id); ++int ath11k_wmi_send_add_update_wds_entry_cmd(struct ath11k *ar, ++ const u8 *peer_addr, const u8 *wds_addr, ++ u8 vdev_id, bool add_wds); + int ath11k_wmi_vdev_delete(struct ath11k *ar, u8 vdev_id); + void ath11k_wmi_start_scan_init(struct ath11k *ar, struct scan_req_params *arg); + int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, diff --git a/package/kernel/mac80211/patches/nss/ath11k/214-ath11k-qos-null-frame-tx-over-wmi.patch b/package/kernel/mac80211/patches/nss/ath11k/214-ath11k-qos-null-frame-tx-over-wmi.patch new file mode 100644 index 00000000000000..5f31f4ded2358c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/214-ath11k-qos-null-frame-tx-over-wmi.patch @@ -0,0 +1,392 @@ +From 0e29b669153f100b60107d5f6b3fe407b71ba79a Mon Sep 17 00:00:00 2001 +From: Sowmiya Sree Elavalagan +Date: Wed, 30 Sep 2020 22:33:42 +0530 +Subject: [PATCH] ath11k: QOS null frame tx over wmi + +Added support to send qos null frame through FW. +NSS driver does not support QOS null frame tx. +Hence this is brought for nss offload case to send +qos null frame. QOS null packet queued from mac80211 +is sent to FW through wmi interface. This happens only if FW supports +qos null tx, this is based on service bit received in ext2 service +event from FW. On successful transmission of QOS null frame status +is set 0 in the event received for this wmi message. This is status +is sent to mac80211 for further handling. + +Signed-off-by: Sowmiya Sree Elavalagan +--- + drivers/net/wireless/ath/ath11k/mac.c | 28 ++++- + drivers/net/wireless/ath/ath11k/wmi.c | 200 ++++++++++++++++++++++++++-------- + drivers/net/wireless/ath/ath11k/wmi.h | 46 +++++++- + 3 files changed, 220 insertions(+), 54 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6129,6 +6129,16 @@ static int ath11k_mac_mgmt_tx_wmi(struct + + ATH11K_SKB_CB(skb)->paddr = paddr; + ++ if (ieee80211_is_qos_nullfunc(hdr->frame_control)) { ++ ret = ath11k_wmi_qos_null_send(ar, arvif->vdev_id, buf_id, skb); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send qos null frame over wmi: %d\n", ret); ++ goto err_unmap_buf; ++ } ++ ++ return 0; ++ } ++ + ret = ath11k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb); + if (ret) { + ath11k_warn(ar->ab, "failed to send mgmt frame: %d\n", ret); +@@ -6196,8 +6206,8 @@ static void ath11k_mgmt_over_wmi_tx_work + } + } + +-static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb, +- bool is_prb_rsp) ++static int ath11k_mac_tx_over_wmi(struct ath11k *ar, struct sk_buff *skb, ++ bool is_prb_rsp) + { + struct sk_buff_head *q = &ar->wmi_mgmt_tx_queue; + +@@ -6259,7 +6269,7 @@ static void ath11k_mac_op_tx(struct ieee + } else if (ieee80211_is_mgmt(hdr->frame_control)) { + frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); + is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); +- ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp); ++ ret = ath11k_mac_tx_over_wmi(ar, skb, is_prb_rsp); + if (ret) { + if (ret != -EBUSY) + ath11k_warn(ar->ab, "failed to queue management frame %d\n", +@@ -6274,6 +6284,20 @@ static void ath11k_mac_op_tx(struct ieee + spin_unlock_bh(&ar->data_lock); + } + return; ++ } else if (ar->ab->nss.enabled && ++ ieee80211_is_qos_nullfunc(hdr->frame_control) && ++ test_bit(WMI_TLV_SERVICE_QOS_NULL_FRAME_TX_OVER_WMI, ++ ar->ab->wmi_ab.svc_map)) { ++ /* NSS driver does not support tx qos null pkt hence it is offload ++ * to fw via wmi path similar to mgmt frames ++ */ ++ ret = ath11k_mac_tx_over_wmi(ar, skb, false); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to queue qos null frame %d\n", ++ ret); ++ ieee80211_free_txskb(ar->hw, skb); ++ } ++ return; + } + + if (control->sta) +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -117,7 +117,7 @@ static const struct wmi_tlv_policy wmi_t + [WMI_TAG_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, + [WMI_TAG_MGMT_TX_COMPL_EVENT] +- = { .min_len = sizeof(struct wmi_mgmt_tx_compl_event) }, ++ = { .min_len = sizeof(struct wmi_tx_compl_event) }, + [WMI_TAG_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TAG_PEER_STA_KICKOUT_EVENT] +@@ -701,6 +701,55 @@ int ath11k_wmi_mgmt_send(struct ath11k * + return ret; + } + ++int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, ++ struct sk_buff *frame) ++{ ++ struct ath11k_pdev_wmi *wmi = ar->wmi; ++ struct wmi_qos_null_tx_cmd *cmd; ++ struct wmi_tlv *frame_tlv; ++ struct sk_buff *skb; ++ u32 buf_len; ++ int len, ret = 0; ++ ++ buf_len = frame->len < WMI_QOS_NULL_SEND_BUF_LEN ? ++ frame->len : WMI_QOS_NULL_SEND_BUF_LEN; ++ ++ len = sizeof(*cmd) + sizeof(*frame_tlv) + roundup(buf_len, 4); ++ ++ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (struct wmi_qos_null_tx_cmd *)skb->data; ++ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_QOS_NULL_FRAME_TX_SEND) | ++ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); ++ cmd->vdev_id = vdev_id; ++ cmd->desc_id = buf_id; ++ cmd->paddr_lo = lower_32_bits(ATH11K_SKB_CB(frame)->paddr); ++ cmd->paddr_hi = upper_32_bits(ATH11K_SKB_CB(frame)->paddr); ++ cmd->frame_len = frame->len; ++ cmd->buf_len = buf_len; ++ ++ frame_tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd)); ++ frame_tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | ++ FIELD_PREP(WMI_TLV_LEN, buf_len); ++ ++ memcpy(frame_tlv->value, frame->data, buf_len); ++ ++ ath11k_ce_byte_swap(frame_tlv->value, buf_len); ++ ++ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_QOS_NULL_FRAME_TX_SEND_CMDID); ++ if (ret) { ++ ath11k_warn(ar->ab, ++ "failed to submit WMI_QOS_NULL_FRAME_TX_SEND_CMDID cmd\n"); ++ dev_kfree_skb(skb); ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_WMI, ++ "wmi QOS null tx send cmd sent successfully\n"); ++ return ret; ++} ++ + int ath11k_wmi_vdev_create(struct ath11k *ar, u8 *macaddr, + struct vdev_create_params *param) + { +@@ -5905,8 +5954,8 @@ static int ath11k_pull_mgmt_rx_params_tl + return 0; + } + +-static int wmi_process_mgmt_tx_comp(struct ath11k *ar, +- struct wmi_mgmt_tx_compl_event *tx_compl_param) ++static int wmi_process_tx_comp(struct ath11k *ar, ++ struct wmi_tx_compl_event *tx_compl_param) + { + struct sk_buff *msdu; + struct ieee80211_tx_info *info; +@@ -5944,6 +5993,11 @@ static int wmi_process_mgmt_tx_comp(stru + info->status.ack_signal = tx_compl_param->ack_rssi; + } + ++ /* dont update rates in this path, qos null data tx completions also can ++ * take this path in case of nss offload and can update invalid rates. ++ */ ++ info->status.rates[0].idx = -1; ++ + hdr = (struct ieee80211_hdr *)msdu->data; + frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); + +@@ -5962,10 +6016,13 @@ static int wmi_process_mgmt_tx_comp(stru + arvif = ath11k_vif_to_arvif(vif); + mgmt_stats = &arvif->mgmt_stats; + +- if (!tx_compl_param->status) +- mgmt_stats->tx_compl_succ[frm_type]++; +- else +- mgmt_stats->tx_compl_fail[frm_type]++; ++ if (ieee80211_is_mgmt(hdr->frame_control)) { ++ if (!tx_compl_param->status) ++ mgmt_stats->tx_compl_succ[frm_type]++; ++ else ++ mgmt_stats->tx_compl_fail[frm_type]++; ++ } ++ + spin_unlock_bh(&ar->data_lock); + + skip_mgmt_stats: +@@ -5987,12 +6044,13 @@ skip_mgmt_stats: + return 0; + } + +-static int ath11k_pull_mgmt_tx_compl_param_tlv(struct ath11k_base *ab, +- struct sk_buff *skb, +- struct wmi_mgmt_tx_compl_event *param) ++static int ath11k_pull_tx_compl_param_tlv(struct ath11k_base *ab, ++ struct sk_buff *skb, ++ struct wmi_tx_compl_event *param, ++ int event_id) + { + const void **tb; +- const struct wmi_mgmt_tx_compl_event *ev; ++ const struct wmi_tx_compl_event *ev; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); +@@ -6002,7 +6060,7 @@ static int ath11k_pull_mgmt_tx_compl_par + return ret; + } + +- ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT]; ++ ev = tb[event_id]; + if (!ev) { + ath11k_warn(ab, "failed to fetch mgmt tx compl ev"); + kfree(tb); +@@ -7810,10 +7868,11 @@ exit: + + static void ath11k_mgmt_tx_compl_event(struct ath11k_base *ab, struct sk_buff *skb) + { +- struct wmi_mgmt_tx_compl_event tx_compl_param = {0}; ++ struct wmi_tx_compl_event tx_compl_param = {0}; + struct ath11k *ar; + +- if (ath11k_pull_mgmt_tx_compl_param_tlv(ab, skb, &tx_compl_param) != 0) { ++ if (ath11k_pull_tx_compl_param_tlv(ab, skb, &tx_compl_param, ++ WMI_TAG_MGMT_TX_COMPL_EVENT) != 0) { + ath11k_warn(ab, "failed to extract mgmt tx compl event"); + return; + } +@@ -7826,7 +7885,7 @@ static void ath11k_mgmt_tx_compl_event(s + goto exit; + } + +- wmi_process_mgmt_tx_comp(ar, &tx_compl_param); ++ wmi_process_tx_comp(ar, &tx_compl_param); + + ath11k_dbg(ab, ATH11K_DBG_MGMT, + "event mgmt tx compl ev pdev_id %d, desc_id %d, status %d ack_rssi %d", +@@ -7837,6 +7896,36 @@ exit: + rcu_read_unlock(); + } + ++static void ath11k_qos_null_compl_event(struct ath11k_base *ab, struct sk_buff *skb) ++{ ++ struct wmi_tx_compl_event tx_compl_param = {0}; ++ struct ath11k *ar; ++ ++ if (ath11k_pull_tx_compl_param_tlv(ab, skb, &tx_compl_param, ++ WMI_TAG_QOS_NULL_FRAME_TX_STATUS) != 0) { ++ ath11k_warn(ab, "failed to extract qos null tx compl event"); ++ return; ++ } ++ ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_pdev_id(ab, tx_compl_param.pdev_id); ++ if (!ar) { ++ ath11k_warn(ab, "invalid pdev id %d in qos_null_tx_compl_event\n", ++ tx_compl_param.pdev_id); ++ goto exit; ++ } ++ ++ wmi_process_tx_comp(ar, &tx_compl_param); ++ ++ ath11k_dbg(ab, ATH11K_DBG_WMI, ++ "QOS null tx compl ev pdev_id %d, desc_id %d, status %d", ++ tx_compl_param.pdev_id, tx_compl_param.desc_id, ++ tx_compl_param.status); ++ ++exit: ++ rcu_read_unlock(); ++} ++ + static struct ath11k *ath11k_get_ar_on_scan_state(struct ath11k_base *ab, + u32 vdev_id, + enum ath11k_scan_state state) +@@ -9024,6 +9113,10 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_WDS_PEER_EVENTID: + ath11k_wmi_wds_peer_event(ab, skb); + break; ++ case WMI_QOS_NULL_FRAME_TX_COMPLETION_EVENTID: ++ ath11k_qos_null_compl_event(ab, skb); ++ break; ++ + default: + ath11k_dbg(ab, ATH11K_DBG_WMI, "unsupported event id 0x%x\n", id); + break; +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -363,6 +363,7 @@ enum wmi_tlv_cmd_id { + WMI_BSS_COLOR_CHANGE_ENABLE_CMDID, + WMI_VDEV_BCN_OFFLOAD_QUIET_CONFIG_CMDID, + WMI_FILS_DISCOVERY_TMPL_CMDID, ++ WMI_QOS_NULL_FRAME_TX_SEND_CMDID, + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, +@@ -693,6 +694,8 @@ enum wmi_tlv_event_id { + WMI_TBTTOFFSET_EXT_UPDATE_EVENTID, + WMI_OFFCHAN_DATA_TX_COMPLETION_EVENTID, + WMI_HOST_FILS_DISCOVERY_EVENTID, ++ WMI_HOST_SWBA_V2_EVENTID, ++ WMI_QOS_NULL_FRAME_TX_COMPLETION_EVENTID, + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_CMD(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + WMI_BA_RSP_SSN_EVENTID, +@@ -1880,6 +1883,9 @@ enum wmi_tlv_tag { + WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, + WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, ++ /* TODO add all the missing cmds */ ++ WMI_TAG_QOS_NULL_FRAME_TX_SEND = 0x3A6, ++ WMI_TAG_QOS_NULL_FRAME_TX_STATUS, + WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, + WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, + WMI_TAG_MAX +@@ -2109,7 +2115,17 @@ enum wmi_tlv_service { + WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT = 246, + WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249, + WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, ++ WMI_TLV_SERVICE_CONFIGURE_ROAM_TRIGGER_PARAM_SUPPORT = 254, ++ WMI_TLV_SERVICE_CFR_TA_RA_AS_FP_SUPPORT = 255, ++ WMI_TLV_SERVICE_CFR_CAPTURE_COUNT_SUPPORT = 256, ++ WMI_TLV_SERVICE_OCV_SUPPORT = 257, ++ WMI_TLV_SERVICE_LL_STATS_PER_CHAN_RX_TX_TIME_SUPPORT = 258, ++ WMI_TLV_SERVICE_THERMAL_MULTI_CLIENT_SUPPORT = 259, ++ WMI_TLV_SERVICE_NAN_SEND_NAN_ENABLE_RESPONSE_TO_HOST = 260, ++ WMI_TLV_SERVICE_UNIFIED_LL_GET_STA_CMD_SUPPORT = 261, ++ WMI_TLV_SERVICE_FSE_CMEM_ALLOC_SUPPORT = 262, + WMI_TLV_SERVICE_PASSIVE_SCAN_START_TIME_ENHANCE = 263, ++ WMI_TLV_SERVICE_QOS_NULL_FRAME_TX_OVER_WMI = 264, + + /* The second 128 bits */ + WMI_MAX_EXT_SERVICE = 256, +@@ -3831,6 +3847,7 @@ struct wmi_scan_prob_req_oui_cmd { + } __packed; + + #define WMI_MGMT_SEND_DOWNLD_LEN 64 ++#define WMI_QOS_NULL_SEND_BUF_LEN 64 + + #define WMI_TX_PARAMS_DWORD0_POWER GENMASK(7, 0) + #define WMI_TX_PARAMS_DWORD0_MCS_MASK GENMASK(19, 8) +@@ -3841,9 +3858,10 @@ struct wmi_scan_prob_req_oui_cmd { + #define WMI_TX_PARAMS_DWORD1_BW_MASK GENMASK(14, 8) + #define WMI_TX_PARAMS_DWORD1_PREAMBLE_TYPE GENMASK(19, 15) + #define WMI_TX_PARAMS_DWORD1_FRAME_TYPE BIT(20) +-#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 21) ++#define WMI_TX_PARAMS_DWORD1_CFR_CAPTURE BIT(21) ++#define WMI_TX_PARAMS_DWORD1_RSVD GENMASK(31, 22) + +-struct wmi_mgmt_send_params { ++struct wmi_tx_send_params { + u32 tlv_header; + u32 tx_params_dword0; + u32 tx_params_dword1; +@@ -4959,7 +4977,7 @@ struct wmi_rssi_ctl_ext { + u32 rssi_ctl_ext[MAX_ANTENNA_EIGHT - ATH_MAX_ANTENNA]; + }; + +-struct wmi_mgmt_tx_compl_event { ++struct wmi_tx_compl_event { + u32 desc_id; + u32 status; + u32 pdev_id; +@@ -5790,6 +5808,17 @@ struct wmi_debug_log_config_cmd_fixed_pa + u32 value; + } __packed; + ++struct wmi_qos_null_tx_cmd { ++ u32 tlv_header; ++ u32 vdev_id; ++ u32 desc_id; ++ u32 paddr_lo; ++ u32 paddr_hi; ++ u32 frame_len; ++ u32 buf_len; ++ u32 tx_params_valid; ++} __packed; ++ + #define WMI_MAX_MEM_REQS 32 + + #define MAX_RADIOS 3 +@@ -6400,6 +6429,8 @@ int ath11k_wmi_cmd_send(struct ath11k_pd + struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); + int ath11k_wmi_mgmt_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, + struct sk_buff *frame); ++int ath11k_wmi_qos_null_send(struct ath11k *ar, u32 vdev_id, u32 buf_id, ++ struct sk_buff *frame); + int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id, + struct ieee80211_mutable_offsets *offs, + struct sk_buff *bcn, u32 ema_param); diff --git a/package/kernel/mac80211/patches/nss/ath11k/235-002-ath11k-add-support-for-ext-vdev-in-NSS-for-AP_VLAN-vif-handling.patch b/package/kernel/mac80211/patches/nss/ath11k/235-002-ath11k-add-support-for-ext-vdev-in-NSS-for-AP_VLAN-vif-handling.patch new file mode 100644 index 00000000000000..7a935968261d33 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/235-002-ath11k-add-support-for-ext-vdev-in-NSS-for-AP_VLAN-vif-handling.patch @@ -0,0 +1,784 @@ +From 83c2a029a5300b2aaeaa9855011668b407d142c2 Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Fri, 20 Nov 2020 11:41:11 +0530 +Subject: [PATCH 2/3] ath11k: add support for ext vdev in NSS for AP_VLAN vif + handling + +- add ext vdev NSS API callbacks required for AP_VLAN vif +- invoke ieee80211_rx_nss_notify_4addr on WDS Rx path for 4addr frames until + ext vdev interface is UP +- do ext vdev down of all AP_VLAN vifs upon vdev down of associated AP vif + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/nss.c | 452 ++++++++++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/nss.h | 57 +++++ + 2 files changed, 495 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -323,6 +323,10 @@ void ath11k_nss_wifili_event_receive(str + ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili wds peer del event received %d response %d error %d\n", + msg_type, response, error); + break; ++ case NSS_WIFILI_PEER_4ADDR_EVENT_MSG: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili peer 4addr event received %d response %d error %d\n", ++ msg_type, response, error); ++ break; + default: + ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type); + break; +@@ -556,8 +560,9 @@ static int ath11k_nss_undecap_nwifi(stru + return 0; + } + +-static void ath11k_nss_wds_type_rx(struct ath11k *ar, u8* src_mac, u8 is_sa_valid, +- u8 addr4_valid, u16 peer_id) ++static void ath11k_nss_wds_type_rx(struct ath11k *ar, struct net_device *dev, ++ u8* src_mac, u8 is_sa_valid, u8 addr4_valid, ++ u16 peer_id, bool *drop) + { + struct ath11k_base *ab = ar->ab; + struct ath11k_ast_entry *ast_entry = NULL; +@@ -579,19 +584,22 @@ static void ath11k_nss_wds_type_rx(struc + if (!is_sa_valid) { + ath11k_peer_add_ast(ar, ta_peer, src_mac, + ATH11K_AST_TYPE_WDS); +- ath11k_nss_add_wds_peer(ar, ta_peer, +- src_mac, ATH11K_AST_TYPE_WDS); ++ if (!ta_peer->nss.ext_vdev_up) ++ ieee80211_rx_nss_notify_4addr(dev, ta_peer->addr); + } else { + if (!ast_entry) { + ath11k_peer_add_ast(ar, ta_peer, src_mac, + ATH11K_AST_TYPE_WDS); +- ath11k_nss_add_wds_peer(ar, ta_peer, src_mac, +- ATH11K_AST_TYPE_WDS); +- } else { ++ if (!ta_peer->nss.ext_vdev_up) ++ ieee80211_rx_nss_notify_4addr(dev, ta_peer->addr); ++ } else if (ast_entry->type == ATH11K_AST_TYPE_WDS) { + ath11k_peer_update_ast(ar, ta_peer, ast_entry); + ath11k_nss_update_wds_peer(ar, ta_peer, src_mac); + } + } ++ ++ if (!ta_peer->nss.ext_vdev_up) ++ *drop = true; + } + + spin_unlock_bh(&ab->base_lock); +@@ -635,7 +643,8 @@ static void ath11k_nss_mec_handler(struc + + static void ath11k_nss_vdev_spl_receive_ext_wdsdata(struct ath11k_vif *arvif, + struct sk_buff *skb, +- struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata) ++ struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata, ++ bool *drop) + { + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; +@@ -656,8 +665,8 @@ static void ath11k_nss_vdev_spl_receive_ + + switch (wds_type) { + case NSS_WIFI_VDEV_WDS_TYPE_RX: +- ath11k_nss_wds_type_rx(ar, src_mac, is_sa_valid, +- addr4_valid, peer_id); ++ ath11k_nss_wds_type_rx(ar, skb->dev, src_mac, is_sa_valid, ++ addr4_valid, peer_id, drop); + break; + case NSS_WIFI_VDEV_WDS_TYPE_MEC: + ath11k_nss_mec_handler(ar, (u8 *)(skb->data)); +@@ -724,6 +733,7 @@ ath11k_nss_vdev_special_data_receive(str + struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_base *ab; ++ bool drop = false; + bool eth_decap = false; + int data_offs = 0; + int ret = 0; +@@ -779,10 +789,11 @@ ath11k_nss_vdev_special_data_receive(str + NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_WDS_LEARN) { + wds_metadata = &wifi_metadata->metadata.wds_metadata; + ath11k_nss_vdev_spl_receive_ext_wdsdata(arvif, skb, +- wds_metadata); ++ wds_metadata, &drop); + } + +- ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++ if (!drop) ++ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); + } + + static void +@@ -845,6 +856,68 @@ ath11k_nss_vdev_data_receive(struct net_ + ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); + } + ++static void ++ath11k_nss_ext_vdev_special_data_receive(struct net_device *dev, ++ struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ dev_kfree_skb_any(skb); ++} ++ ++static void ++ath11k_nss_ext_vdev_data_receive(struct net_device *dev, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ struct wireless_dev *wdev; ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ bool eth_decap = false; ++ int data_offs = 0; ++ int ret; ++ ++ if (!dev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ wdev = dev->ieee80211_ptr; ++ if (!wdev) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ arvif = (struct ath11k_vif *)vif->drv_priv; ++ if (!arvif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ /* log the original skb received from nss */ ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss ext : ", ++ skb->data, skb->len); ++ ++ ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap); ++ if (ret) { ++ ath11k_warn(ab, "error in nss ext rx undecap, type %d err %d\n", ++ arvif->nss.decap, ret); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++} ++ + int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb) + { + struct ath11k *ar = arvif->ar; +@@ -867,10 +940,16 @@ int ath11k_nss_tx(struct ath11k_vif *arv + ath11k_nss_tx_encap_nwifi(skb); + + send: +- ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "", "nss tx msdu: ", +- skb->data, skb->len); +- +- status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, arvif->nss.if_num); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, ++ arvif->vif->type == NL80211_IFTYPE_AP_VLAN ? "ext vdev" : "", ++ "nss tx msdu: ", skb->data, skb->len); ++ ++ if (arvif->vif->type == NL80211_IFTYPE_AP_VLAN) ++ status = nss_wifi_ext_vdev_tx_buf(arvif->nss.ctx, skb, ++ arvif->nss.if_num); ++ else ++ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, ++ arvif->nss.if_num); + + if (status != NSS_TX_SUCCESS) { + ath11k_dbg(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX), +@@ -1211,6 +1290,7 @@ int ath11k_nss_vdev_up(struct ath11k_vif + struct nss_wifi_vdev_msg *vdev_msg = NULL; + struct nss_wifi_vdev_enable_msg *vdev_en; + struct ath11k *ar = arvif->ar; ++ struct ath11k_vif *ap_vlan_arvif, *tmp; + nss_tx_status_t status; + int ret = 0; + +@@ -1242,6 +1322,12 @@ int ath11k_nss_vdev_up(struct ath11k_vif + } + + ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev up tx msg success\n"); ++ ++ if (arvif->vif->type == NL80211_IFTYPE_AP) ++ list_for_each_entry_safe(ap_vlan_arvif, tmp, &arvif->ap_vlan_arvifs, ++ list) ++ if (ap_vlan_arvif->nss.added) ++ ath11k_nss_ext_vdev_up(ap_vlan_arvif); + free: + kfree(vdev_msg); + return ret; +@@ -1251,6 +1337,7 @@ int ath11k_nss_vdev_down(struct ath11k_v + { + struct nss_wifi_vdev_msg *vdev_msg = NULL; + struct ath11k *ar = arvif->ar; ++ struct ath11k_vif *ap_vlan_arvif, *tmp; + nss_tx_status_t status; + int ret = 0; + +@@ -1278,11 +1365,362 @@ int ath11k_nss_vdev_down(struct ath11k_v + } + + ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev down tx msg success\n"); ++ ++ if (arvif->vif->type == NL80211_IFTYPE_AP) ++ list_for_each_entry_safe(ap_vlan_arvif, tmp, &arvif->ap_vlan_arvifs, ++ list) ++ ath11k_nss_ext_vdev_down(ap_vlan_arvif); + free: + kfree(vdev_msg); + return ret; + } + ++int ath11k_nss_ext_vdev_cfg_wds_peer(struct ath11k_vif *arvif, ++ u8 *wds_addr, u32 wds_peer_id) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct nss_wifi_ext_vdev_msg *ext_vdev_msg = NULL; ++ struct nss_wifi_ext_vdev_wds_msg *cfg_wds_msg = NULL; ++ nss_tx_status_t status; ++ int ret; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ ext_vdev_msg = kzalloc(sizeof(struct nss_wifi_ext_vdev_msg), GFP_ATOMIC); ++ if (!ext_vdev_msg) ++ return -ENOMEM; ++ ++ cfg_wds_msg = &ext_vdev_msg->msg.wmsg; ++ cfg_wds_msg->wds_peer_id = wds_peer_id; ++ ether_addr_copy((u8 *)cfg_wds_msg->mac_addr, wds_addr); ++ ++ nss_wifi_ext_vdev_msg_init(ext_vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_EXT_VDEV_MSG_CONFIGURE_WDS, ++ sizeof(struct nss_wifi_ext_vdev_wds_msg), ++ NULL, arvif); ++ ++ status = nss_wifi_ext_vdev_tx_msg_sync(arvif->nss.ctx, ext_vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "failed to configure wds peer nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(ext_vdev_msg); ++ ++ return ret; ++} ++ ++int ath11k_nss_ext_vdev_wds_4addr_allow(struct ath11k_vif *arvif, ++ u32 wds_peer_id) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct nss_wifili_peer_wds_4addr_allow_msg *cfg_4addr_msg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ struct nss_wifili_msg *wlmsg; ++ nss_tx_status_t status; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ cfg_4addr_msg = &wlmsg->msg.wpswm; ++ cfg_4addr_msg->peer_id = wds_peer_id; ++ cfg_4addr_msg->if_num = arvif->nss.if_num; ++ cfg_4addr_msg->enable = true; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, ++ NSS_WIFILI_PEER_4ADDR_EVENT_MSG, ++ sizeof(struct nss_wifili_peer_wds_4addr_allow_msg), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ar->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss wds 4addr allow if_num %d, peer_id %d cfg fail: %d\n", ++ arvif->nss.if_num, wds_peer_id, status); ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_WDS, "nss wds 4addr allow if_num %d, peer_id %d cfg complete\n", ++ arvif->nss.if_num, wds_peer_id); ++free: ++ kfree(wlmsg); ++ return status; ++} ++ ++int ath11k_nss_ext_vdev_configure(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_vif *ap_vif = arvif->nss.ap_vif; ++ struct nss_wifi_ext_vdev_msg *ext_vdev_msg = NULL; ++ struct nss_wifi_ext_vdev_configure_if_msg *ext_vdev_cfg = NULL; ++ nss_tx_status_t status; ++ int ret; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ ext_vdev_msg = kzalloc(sizeof(struct nss_wifi_ext_vdev_msg), GFP_ATOMIC); ++ if (!ext_vdev_msg) ++ return -ENOMEM; ++ ++ ext_vdev_cfg = &ext_vdev_msg->msg.cmsg; ++ ++ ext_vdev_cfg->radio_ifnum = ar->nss.if_num; ++ ext_vdev_cfg->pvap_ifnum = ap_vif->nss.if_num; ++ ++ ether_addr_copy(ext_vdev_cfg->mac_addr, arvif->vif->addr); ++ ++ nss_wifi_ext_vdev_msg_init(ext_vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_EXT_VDEV_MSG_CONFIGURE_IF, ++ sizeof(struct nss_wifi_ext_vdev_configure_if_msg), ++ NULL, arvif); ++ ++ status = nss_wifi_ext_vdev_tx_msg_sync(arvif->ar->nss.ctx, ext_vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "failed to configure nss ext vdev nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(ext_vdev_msg); ++ ++ return ret; ++} ++ ++void ath11k_nss_ext_vdev_unregister(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return; ++ ++ nss_wifi_ext_vdev_unregister_if(arvif->nss.if_num); ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "unregistered nss vdev %d \n", ++ arvif->nss.if_num); ++} ++ ++static int ath11k_nss_ext_vdev_register(struct ath11k_vif *arvif, ++ struct net_device *netdev) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ nss_tx_status_t status; ++ enum nss_dynamic_interface_type di_type; ++ u32 features = 0; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN || arvif->nss.ctx) ++ return -EINVAL; ++ ++ di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS; ++ ++ arvif->nss.ctx = nss_wifi_ext_vdev_register_if(arvif->nss.if_num, ++ ath11k_nss_ext_vdev_data_receive, ++ ath11k_nss_ext_vdev_special_data_receive, ++ NULL, netdev, features, ++ arvif); ++ ++ if (!arvif->nss.ctx) { ++ ath11k_warn(ab, "failed to register nss vdev if_num %d nss_err:%d\n", ++ arvif->nss.if_num, status); ++ return -EINVAL; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "registered nss ext vdev if_num %d\n", ++ arvif->nss.if_num); ++ return 0; ++} ++ ++static void ath11k_nss_ext_vdev_free(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ nss_tx_status_t status; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return; ++ ++ status = nss_dynamic_interface_dealloc_node( ++ arvif->nss.if_num, ++ NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS); ++ if (status != NSS_TX_SUCCESS) ++ ath11k_warn(ab, "failed to free nss ext vdev err:%d\n", ++ status); ++ else ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss ext vdev interface deallocated\n"); ++} ++ ++static int ath11k_nss_ext_vdev_alloc(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ enum nss_dynamic_interface_type di_type; ++ int if_num; ++ ++ di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS; ++ ++ if_num = nss_dynamic_interface_alloc_node(di_type); ++ if (if_num < 0) { ++ ath11k_warn(ab, "failed to allocate nss ext vdev\n"); ++ return -EINVAL; ++ } ++ ++ arvif->nss.if_num = if_num; ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss ext vdev interface %pM allocated if_num %d\n", ++ arvif->vif->addr, if_num); ++ ++ return 0; ++} ++ ++int ath11k_nss_ext_vdev_create(struct ath11k_vif *arvif) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ struct wireless_dev *wdev; ++ int ret; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ if (arvif->nss.created) ++ return 0; ++ ++ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif); ++ if (!wdev) { ++ ath11k_warn(ab, "ath11k_nss: ext wdev is null\n"); ++ return -EINVAL; ++ } ++ ++ if (!wdev->netdev) { ++ ath11k_warn(ab, "ath11k_nss: ext netdev is null\n"); ++ return -EINVAL; ++ } ++ ++ ret = ath11k_nss_ext_vdev_alloc(arvif); ++ if (ret) ++ return ret; ++ ++ ret = ath11k_nss_ext_vdev_register(arvif, wdev->netdev); ++ if (ret) ++ goto free_ext_vdev; ++ ++ arvif->nss.created = true; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, ++ "nss ext vdev interface created ctx %pK, ifnum %d\n", ++ arvif->nss.ctx, arvif->nss.if_num); ++ ++ return ret; ++ ++free_ext_vdev: ++ ath11k_nss_ext_vdev_free(arvif); ++ ++ return ret; ++} ++ ++void ath11k_nss_ext_vdev_delete(struct ath11k_vif *arvif) ++{ ++ if (!arvif->ar->ab->nss.enabled) ++ return; ++ ++ if (!arvif->nss.created) ++ return; ++ ++ ath11k_dbg(arvif->ar->ab, ATH11K_DBG_NSS_WDS, ++ "nss ext vdev interface delete ctx %pK, ifnum %d\n", ++ arvif->nss.ctx, arvif->nss.if_num); ++ ++ ath11k_nss_ext_vdev_unregister(arvif); ++ ++ ath11k_nss_ext_vdev_free(arvif); ++ ++ arvif->nss.created = false; ++} ++ ++int ath11k_nss_ext_vdev_up(struct ath11k_vif *arvif) ++{ ++ struct nss_wifi_ext_vdev_msg *ext_vdev_msg = NULL; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ if (arvif->nss.ext_vdev_up) ++ return 0; ++ ++ ext_vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); ++ if (!ext_vdev_msg) ++ return -ENOMEM; ++ ++ nss_wifi_ext_vdev_msg_init(ext_vdev_msg, arvif->nss.if_num, NSS_IF_OPEN, ++ sizeof(struct nss_if_open), NULL, arvif); ++ ++ status = nss_wifi_ext_vdev_tx_msg_sync(arvif->nss.ctx, ext_vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss ext vdev up tx msg error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_WDS, "nss ext vdev up tx msg success\n"); ++ arvif->nss.ext_vdev_up = true; ++free: ++ kfree(ext_vdev_msg); ++ return ret; ++} ++ ++int ath11k_nss_ext_vdev_down(struct ath11k_vif *arvif) ++{ ++ struct nss_wifi_ext_vdev_msg *ext_vdev_msg = NULL; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ ext_vdev_msg = kzalloc(sizeof(struct nss_wifi_ext_vdev_msg), GFP_ATOMIC); ++ if (!ext_vdev_msg) ++ return -ENOMEM; ++ ++ nss_wifi_ext_vdev_msg_init(ext_vdev_msg, arvif->nss.if_num, NSS_IF_CLOSE, ++ sizeof(struct nss_if_close), NULL, arvif); ++ ++ status = nss_wifi_ext_vdev_tx_msg_sync(arvif->nss.ctx, ext_vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss ext vdev down tx msg error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_WDS, "nss ext vdev down tx msg success\n"); ++ ++ arvif->nss.ext_vdev_up = false; ++free: ++ kfree(ext_vdev_msg); ++ return ret; ++} ++ + /*----------------------------Peer Setup/Config -----------------------------*/ + + int ath11k_nss_set_peer_sec_type(struct ath11k *ar, +@@ -1376,22 +1814,22 @@ free: + return status; + } + +-void ath11k_nss_update_sta_stats(struct station_info *sinfo, +- struct ieee80211_sta *sta, +- struct ath11k_sta *arsta) ++void ath11k_nss_update_sta_stats(struct ath11k_vif *arvif, ++ struct station_info *sinfo, ++ struct ieee80211_sta *sta) + { + struct sta_info *stainfo; + struct ath11k_peer *peer; +- struct ath11k *ar = arsta->arvif->ar; ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + +- if (!ab->nss.enabled) ++ if (!ab->nss.stats_enabled) + return; + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_addr(arsta->arvif->ar->ab, sta->addr); ++ peer = ath11k_peer_find_by_addr(ab, sta->addr); + if (!peer) { +- ath11k_dbg(ab, ATH11K_DBG_NSS, "unable to find peer %pM\n", ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "sta stats: unable to find peer %pM\n", + sta->addr); + goto exit; + } +@@ -1463,13 +1901,13 @@ void ath11k_nss_update_sta_rxrate(struct + struct ath11k_peer *peer, + struct hal_rx_user_status *user_stats) + { +- struct ath11k_sta *arsta = (struct ath11k_sta *)peer->sta->drv_priv; + u16 ath11k_hal_rx_legacy_rates[] = + { 10, 20, 55, 60, 90, 110, 120, 180, 240, 360, 480, 540 }; + u16 rate = 0; + u32 preamble_type; + u8 mcs, nss; +- struct ath11k *ar = arsta->arvif->ar; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(peer->vif); ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + + if (!ab->nss.enabled) +@@ -1773,8 +2211,8 @@ int ath11k_nss_add_wds_peer(struct ath11 + } + + ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, +- "nss add wds peer success peer mac:%pM dest mac:%pM peer_id:%d\n", +- wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); ++ "nss add wds peer success pdev:%d peer mac:%pM dest mac:%pM peer_id:%d\n", ++ wds_peer_msg->pdev_id, wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); + + msg_free: + kfree(wlmsg); +@@ -1819,8 +2257,8 @@ int ath11k_nss_update_wds_peer(struct at + } + + ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, +- "nss update wds peer success peer mac:%pM dest mac:%pM peer_id:%d\n", +- wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); ++ "nss update wds peer success pdev:%d peer mac:%pM dest mac:%pM peer_id:%d\n", ++ wds_peer_msg->pdev_id, wds_peer_msg->peer_mac, wds_peer_msg->dest_mac, wds_peer_msg->peer_id); + + msg_free: + kfree(wlmsg); +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -151,6 +151,7 @@ enum ath11k_nss_peer_sec_type { + struct ath11k_nss_peer { + uint32_t *vaddr; + dma_addr_t paddr; ++ bool ext_vdev_up; + struct peer_stats *nss_stats; + struct completion complete; + }; +@@ -165,6 +166,16 @@ struct arvif_nss { + int encap; + /* Keep the copy of decap type for nss */ + int decap; ++ /* AP_VLAN vif context obtained on ext vdev register */ ++ void* ctx; ++ /* Parent AP vif stored in case of AP_VLAN vif */ ++ struct ath11k_vif *ap_vif; ++ /* Flag to notify if vlan arvif object is added to arvif list*/ ++ bool added; ++ /* Flag to notify if ext vdev is up/down */ ++ bool ext_vdev_up; ++ /* WDS cfg should be done only once for ext vdev */ ++ bool wds_cfg_done; + bool created; + }; + +@@ -220,11 +231,21 @@ int ath11k_nss_map_wds_peer(struct ath11 + u8 *dest_mac, enum ath11k_ast_entry_type type); + int ath11k_nss_del_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, + u8 *dest_mac); ++int ath11k_nss_ext_vdev_cfg_wds_peer(struct ath11k_vif *arvif, ++ u8 *wds_addr, u32 wds_peer_id); ++int ath11k_nss_ext_vdev_wds_4addr_allow(struct ath11k_vif *arvif, ++ u32 wds_peer_id); ++int ath11k_nss_ext_vdev_create(struct ath11k_vif *arvif); ++int ath11k_nss_ext_vdev_configure(struct ath11k_vif *arvif); ++void ath11k_nss_ext_vdev_unregister(struct ath11k_vif *arvif); ++int ath11k_nss_ext_vdev_up(struct ath11k_vif *arvif); ++int ath11k_nss_ext_vdev_down(struct ath11k_vif *arvif); ++void ath11k_nss_ext_vdev_delete(struct ath11k_vif *arvif); + int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer, + struct ieee80211_key_conf *key_conf); +-void ath11k_nss_update_sta_stats(struct station_info *sinfo, +- struct ieee80211_sta *sta, +- struct ath11k_sta *arsta); ++void ath11k_nss_update_sta_stats(struct ath11k_vif *arvif, ++ struct station_info *sinfo, ++ struct ieee80211_sta *sta); + void ath11k_nss_update_sta_rxrate(struct hal_rx_mon_ppdu_info *ppdu_info, + struct ath11k_peer *peer, + struct hal_rx_user_status *user_stats); +@@ -257,9 +278,9 @@ static inline void ath11k_nss_vdev_delet + { + } + +-static inline void ath11k_nss_update_sta_stats(struct station_info *sinfo, +- struct ieee80211_sta *sta, +- struct ath11k_sta *arsta) ++static inline void ath11k_nss_update_sta_stats(struct ath11k_vif *arvif, ++ struct station_info *sinfo, ++ struct ieee80211_sta *sta) + { + return; + } +@@ -316,6 +337,43 @@ static inline int ath11k_nss_peer_create + return 0; + } + ++static inline int ath11k_nss_ext_vdev_cfg_wds_peer(struct ath11k_vif *arvif, ++ u8 *wds_addr, u32 wds_peer_id) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_ext_vdev_wds_4addr_allow(struct ath11k_vif *arvif, ++ u32 wds_peer_id) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_ext_vdev_create(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_ext_vdev_configure(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline void ath11k_nss_ext_vdev_unregister(struct ath11k_vif *arvif) ++{ ++ return; ++} ++ ++static inline int ath11k_nss_ext_vdev_up(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_ext_vdev_down(struct ath11k_vif *arvif) ++{ ++ return 0; ++} ++ + static inline void ath11k_nss_peer_stats_enable(struct ath11k *ar) + { + return; +@@ -337,6 +395,11 @@ static inline int ath11k_nss_setup(struc + return 0; + } + ++static inline void ath11k_nss_ext_vdev_delete(struct ath11k_vif *arvif) ++{ ++ return; ++} ++ + static inline int ath11k_nss_teardown(struct ath11k_base *ab) + { + return 0; diff --git a/package/kernel/mac80211/patches/nss/ath11k/235-003-ath11k-add-AP_VLAN-vif-support-for-WDS-offload-in-NSS-offload.patch b/package/kernel/mac80211/patches/nss/ath11k/235-003-ath11k-add-AP_VLAN-vif-support-for-WDS-offload-in-NSS-offload.patch new file mode 100644 index 00000000000000..1730beea85dfc0 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/235-003-ath11k-add-AP_VLAN-vif-support-for-WDS-offload-in-NSS-offload.patch @@ -0,0 +1,572 @@ +From 730f568af3fac2f31467ea0ff374ea442ddc379f Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Fri, 20 Nov 2020 11:57:36 +0530 +Subject: [PATCH 3/3] ath11k: add AP_VLAN vif support for WDS offload in NSS + offload + +- AP_VLAN vif support is required for WDS offload to interop with mac80211 + based 4addr STA and also for multicast-to-unicast conversion of 3addr + multicast to 4addr frames for each associated 4addr STA. + +- For each associated 4addr STA, corresponding AP_VLAN vif having same MAC + address as AP vif is created from hostapd upon 4addr rx_notify from NSS RX. + +- AP_VLAN vif support is added to add/remove interface mac80211 callbacks only + for NSS ext vdev handling and vdev_id, FW vdev operations are not needed. + +- mac80211 advertises AP_VLAN vif for sta_use_4addr drv callback in case of + NSS offload. Extending ath11k_mac_op_sta_use_4addr to invoke ext vdev NSS + APIs required for AP WDS handling. + +- Maintain AP_VLAN vif(s) list on corresponding AP vif and vice versa required + for ext vdev operations (VDEV_DOWN, DELETE, CONFIGURE_IF). + +- NSS require ENABLE_NAWDS and WDS_BACKHAUL to be configured for AP_VLAN + support via ext vdev. + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 156 +++++++++++++++++++++++++++++++-- + drivers/net/wireless/ath/ath11k/wmi.h | 2 + + 3 files changed, 154 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -389,6 +389,7 @@ struct ath11k_vif { + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + struct arvif_nss nss; + #endif ++ struct list_head ap_vlan_arvifs; + }; + + struct ath11k_vif_iter { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4739,6 +4739,11 @@ static void ath11k_sta_rc_update_wk(stru + arvif = arsta->arvif; + ar = arvif->ar; + ++ if (ar->ab->nss.enabled && ++ arsta->arvif->vif->type == NL80211_IFTYPE_AP_VLAN && ++ arsta->use_4addr_set) ++ arvif = arvif->nss.ap_vif; ++ + if (WARN_ON(ath11k_mac_vif_chan(arvif->vif, &def))) + return; + +@@ -4910,17 +4915,28 @@ err_rc_bw_changed: + static void ath11k_sta_set_4addr_wk(struct work_struct *wk) + { + struct ath11k *ar; +- struct ath11k_vif *arvif; ++ struct ath11k_vif *arvif, *ap_vlan_arvif = NULL; ++ struct ieee80211_vif *vif; + struct ath11k_sta *arsta; + struct ieee80211_sta *sta; ++ struct ath11k_base *ab; ++ struct ath11k_peer *wds_peer; ++ u8 wds_addr[ETH_ALEN]; ++ u32 wds_peer_id; + int ret = 0; + + arsta = container_of(wk, struct ath11k_sta, set_4addr_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arvif = arsta->arvif; + ar = arvif->ar; ++ ab = ar->ab; + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ if (ab->nss.enabled && arvif->vif->type == NL80211_IFTYPE_AP_VLAN) { ++ ap_vlan_arvif = arsta->arvif; ++ arvif = ap_vlan_arvif->nss.ap_vif; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, + "setting USE_4ADDR for peer %pM\n", sta->addr); + + ret = ath11k_wmi_set_peer_param(ar, sta->addr, +@@ -4928,8 +4944,93 @@ static void ath11k_sta_set_4addr_wk(stru + WMI_PEER_USE_4ADDR, 1); + + if (ret) +- ath11k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n", ++ ath11k_warn(ab, "failed to set 4addr for STA %pM: %d\n", + sta->addr, ret); ++ ++ if (!ab->nss.enabled || !ap_vlan_arvif) ++ return; ++ ++ vif = ap_vlan_arvif->vif; ++ ++ spin_lock_bh(&ab->base_lock); ++ wds_peer = ath11k_peer_find_by_addr(ab, sta->addr); ++ if (!wds_peer) { ++ spin_unlock_bh(&ab->base_lock); ++ ath11k_warn(ab, "mac sta use 4addr failed to find peer %pM\n", ++ sta->addr); ++ return; ++ } ++ ++ wds_peer_id = wds_peer->peer_id; ++ ether_addr_copy(wds_addr, wds_peer->addr); ++ spin_unlock_bh(&ab->base_lock); ++ ++ /* skip NSS ext vdev registration if already done */ ++ if (ap_vlan_arvif->nss.wds_cfg_done) ++ goto skip_nss_ext; ++ ++ ret = ath11k_nss_ext_vdev_configure(ap_vlan_arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to nss cfg ext vdev %pM: %d\n", ++ vif->addr, ret); ++ goto ext_vdev_delete; ++ } ++ ++ ap_vlan_arvif->nss.wds_cfg_done = true; ++ ++skip_nss_ext: ++ ++ ret = ath11k_nss_ext_vdev_cfg_wds_peer(ap_vlan_arvif, ++ wds_addr, wds_peer_id); ++ if (ret) { ++ ath11k_warn(ab, "failed to nss cfg_wds_peer %pM on %pM: %d\n", ++ sta->addr, vif->addr, ret); ++ goto ext_vdev_delete; ++ } ++ ++ ret = ath11k_nss_ext_vdev_wds_4addr_allow(ap_vlan_arvif, ++ wds_peer_id); ++ if (ret) { ++ ath11k_warn(ab, "failed to nss 4addr allow %pM: %d\n", ++ vif->addr, ret); ++ goto ext_vdev_delete; ++ } ++ ++ ret = ath11k_nss_ext_vdev_up(ap_vlan_arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to nss ext vdev up %pM: %d\n", ++ vif->addr, ret); ++ goto ext_vdev_delete; ++ } ++ ++ spin_lock_bh(&ab->base_lock); ++ wds_peer->nss.ext_vdev_up = true; ++ wds_peer->nss.ext_vif = vif; ++ spin_unlock_bh(&ab->base_lock); ++ ++ /* NAWDS and CFG_WDS_BACKHAUL configs should be done on corresponding ++ * AP vif of the AP_VLAN vif ++ */ ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ WMI_VDEV_PARAM_AP_ENABLE_NAWDS, ++ MIN_IDLE_INACTIVE_TIME_SECS); ++ if (ret) { ++ ath11k_warn(ab, "failed to set vdev %i nawds parameters: %d\n", ++ arvif->vdev_id, ret); ++ goto ext_vdev_down; ++ } ++ ++ return; ++ ++ext_vdev_down: ++ ath11k_nss_ext_vdev_down(ap_vlan_arvif); ++ext_vdev_delete: ++ ath11k_nss_ext_vdev_delete(ap_vlan_arvif); ++ ++ spin_lock_bh(&ar->data_lock); ++ list_del(&ap_vlan_arvif->list); ++ spin_unlock_bh(&ar->data_lock); ++ ap_vlan_arvif->nss.added = false; + } + + static int ath11k_mac_inc_num_stations(struct ath11k_vif *arvif, +@@ -5259,9 +5360,32 @@ static void ath11k_mac_op_sta_set_4addr( + struct ieee80211_sta *sta, bool enabled) + { + struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); ++ struct ath11k_vif *ap_arvif = NULL; + + if (enabled && !arsta->use_4addr_set) { ++ if (ar->ab->nss.enabled && vif->type == NL80211_IFTYPE_AP_VLAN) { ++ /* 4addr STA is initially associated to AP vif, change ++ * it to AP_VLAN vif and add AP_VLAN vif to AP vifs list ++ */ ++ ap_arvif = arsta->arvif; ++ arvif->nss.ap_vif = ap_arvif; ++ ++ /* Check if the vlan arvif object was already present in the ++ * list. We can receive this path multiple times for the same ++ * vlan vif for different sta objects ++ */ ++ if (!arvif->nss.added) { ++ spin_lock_bh(&ar->data_lock); ++ list_add(&arvif->list, &ap_arvif->ap_vlan_arvifs); ++ spin_unlock_bh(&ar->data_lock); ++ arvif->nss.added = true; ++ } ++ ++ arsta->arvif = arvif; ++ } ++ + ieee80211_queue_work(ar->hw, &arsta->set_4addr_wk); + arsta->use_4addr_set = true; + } +@@ -6641,6 +6765,9 @@ static int ath11k_mac_op_update_vif_offl + u32 param_id, param_value; + int ret; + ++ if (ab->nss.enabled && vif->type == NL80211_IFTYPE_AP_VLAN) ++ return 0; ++ + param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE; + if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET || + (vif->type != NL80211_IFTYPE_STATION && +@@ -6861,7 +6988,8 @@ static int ath11k_mac_op_add_interface(s + goto err; + } + +- if (ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) { ++ if (vif->type != NL80211_IFTYPE_AP_VLAN && ++ ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) { + ath11k_warn(ab, "failed to create vdev %u, reached max vdev limit %d\n", + ar->num_created_vdevs, TARGET_NUM_VDEVS(ab)); + ret = -EBUSY; +@@ -6881,6 +7009,28 @@ static int ath11k_mac_op_add_interface(s + arvif->vif = vif; + + INIT_LIST_HEAD(&arvif->list); ++ ++ if ((vif->type == NL80211_IFTYPE_AP_VLAN || ++ vif->type == NL80211_IFTYPE_STATION) && ab->nss.enabled) { ++ if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET && ++ ieee80211_set_hw_80211_encap(vif, true)) { ++ vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; ++ arvif->nss.encap = ATH11K_HW_TXRX_ETHERNET; ++ arvif->nss.decap = ATH11K_HW_TXRX_ETHERNET; ++ } ++ ++ if (vif->type == NL80211_IFTYPE_AP_VLAN) { ++ ret = ath11k_nss_ext_vdev_create(arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to create ext vdev %pM: %d\n", ++ vif->addr, ret); ++ goto err; ++ } ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++ } ++ } ++ + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath11k_mac_vif_sta_connection_loss_work); + +@@ -6910,6 +7060,7 @@ static int ath11k_mac_op_add_interface(s + fallthrough; + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; ++ INIT_LIST_HEAD(&arvif->ap_vlan_arvifs); + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; +@@ -7132,13 +7283,30 @@ static void ath11k_mac_op_remove_interfa + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_base *ab = ar->ab; ++ struct ath11k_vif *ap_vlan_arvif, *tmp; + int ret; + int i; + +- cancel_delayed_work_sync(&arvif->connection_loss_work); +- + mutex_lock(&ar->conf_mutex); + ++ if (vif->type == NL80211_IFTYPE_AP_VLAN) { ++ ath11k_nss_ext_vdev_delete(arvif); ++ ++ /* In case the vlan vif never got added into the ap vlan arvifs ++ * list, avoid removal here ++ */ ++ if (!arvif->nss.added) ++ goto unlock; ++ ++ spin_lock_bh(&ar->data_lock); ++ list_del(&arvif->list); ++ spin_unlock_bh(&ar->data_lock); ++ ++ goto unlock; ++ } ++ ++ cancel_delayed_work_sync(&arvif->connection_loss_work); ++ + ath11k_dbg(ab, ATH11K_DBG_MAC, "remove interface (vdev %d)\n", + arvif->vdev_id); + +@@ -7155,6 +7323,14 @@ static void ath11k_mac_op_remove_interfa + if (ret) + ath11k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n", + arvif->vdev_id, ret); ++ ++ list_for_each_entry_safe(ap_vlan_arvif, tmp, &arvif->ap_vlan_arvifs, ++ list) { ++ ath11k_nss_ext_vdev_delete(ap_vlan_arvif); ++ spin_lock_bh(&ar->data_lock); ++ list_del(&ap_vlan_arvif->list); ++ spin_unlock_bh(&ar->data_lock); ++ } + } + + ret = ath11k_mac_vdev_delete(ar, arvif); +@@ -7198,8 +7374,7 @@ err_vdev_del: + + ath11k_debugfs_remove_interface(arvif); + +- /* TODO: recal traffic pause state based on the available vdevs */ +- ++unlock: + mutex_unlock(&ar->conf_mutex); + } + +@@ -7259,16 +7434,17 @@ static int ath11k_mac_op_ampdu_action(st + struct ieee80211_ampdu_params *params) + { + struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + int ret = -EINVAL; + + mutex_lock(&ar->conf_mutex); + + switch (params->action) { + case IEEE80211_AMPDU_RX_START: +- ret = ath11k_dp_rx_ampdu_start(ar, params); ++ ret = ath11k_dp_rx_ampdu_start(arvif, params); + break; + case IEEE80211_AMPDU_RX_STOP: +- ret = ath11k_dp_rx_ampdu_stop(ar, params); ++ ret = ath11k_dp_rx_ampdu_stop(arvif, params); + break; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: +@@ -8791,6 +8967,7 @@ static void ath11k_mac_op_sta_statistics + { + struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); + struct ath11k *ar = arsta->arvif->ar; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + s8 signal; + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ar->ab->wmi_ab.svc_map); +@@ -8847,7 +9024,8 @@ static void ath11k_mac_op_sta_statistics + ATH11K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); + +- ath11k_nss_update_sta_stats(sinfo, sta, arsta); ++ if (arvif->ar->ab->nss.enabled) ++ ath11k_nss_update_sta_stats(arvif, sinfo, sta); + } + + #if IS_ENABLED(CONFIG_IPV6) +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -5082,6 +5082,8 @@ enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_MESH_11S, + }; + ++#define MIN_IDLE_INACTIVE_TIME_SECS 256 ++ + enum wmi_sta_powersave_param { + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1121,12 +1121,13 @@ err_mem_free: + return ret; + } + +-int ath11k_dp_rx_ampdu_start(struct ath11k *ar, ++int ath11k_dp_rx_ampdu_start(struct ath11k_vif *arvif, + struct ieee80211_ampdu_params *params) + { ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta); +- int vdev_id = arsta->arvif->vdev_id; ++ int vdev_id = arvif->vdev_id; + int ret; + + ret = ath11k_peer_rx_tid_setup(ar, params->sta->addr, vdev_id, +@@ -1138,13 +1139,13 @@ int ath11k_dp_rx_ampdu_start(struct ath1 + return ret; + } + +-int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, ++int ath11k_dp_rx_ampdu_stop(struct ath11k_vif *arvif, + struct ieee80211_ampdu_params *params) + { ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer; +- struct ath11k_sta *arsta = ath11k_sta_to_arsta(params->sta); +- int vdev_id = arsta->arvif->vdev_id; ++ int vdev_id = arvif->vdev_id; + dma_addr_t paddr; + bool active; + int ret; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -88,9 +88,9 @@ static inline u32 ath11k_he_gi_to_nl8021 + return ret; + } + +-int ath11k_dp_rx_ampdu_start(struct ath11k *ar, ++int ath11k_dp_rx_ampdu_start(struct ath11k_vif *arvif, + struct ieee80211_ampdu_params *params); +-int ath11k_dp_rx_ampdu_stop(struct ath11k *ar, ++int ath11k_dp_rx_ampdu_stop(struct ath11k_vif *arvif, + struct ieee80211_ampdu_params *params); + int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif, + const u8 *peer_addr, +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -140,6 +140,24 @@ struct ath11k_ast_entry *ath11k_peer_ast + return NULL; + } + ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_pdev_idx(struct ath11k *ar, ++ u8* addr) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_ast_entry *ast_entry; ++ struct ath11k_peer *peer; ++ ++ lockdep_assert_held(&ab->base_lock); ++ ++ list_for_each_entry(peer, &ab->peers, list) ++ list_for_each_entry(ast_entry, &peer->ast_entry_list, ase_list) ++ if (ether_addr_equal(ast_entry->addr, addr) && ++ ast_entry->pdev_idx == ar->pdev_idx) ++ return ast_entry; ++ ++ return NULL; ++} ++ + void ath11k_peer_ast_wds_wmi_wk(struct work_struct *wk) + { + struct ath11k_ast_entry *ast_entry = container_of(wk, +@@ -200,8 +218,8 @@ int ath11k_peer_add_ast(struct ath11k *a + } + + if (type != ATH11K_AST_TYPE_STATIC) { +- ast_entry = ath11k_peer_ast_find_by_addr(ab, mac_addr); +- if (ast_entry) { ++ ast_entry = ath11k_peer_ast_find_by_pdev_idx(ar, mac_addr); ++ if (ast_entry && ast_entry->type != ATH11K_AST_TYPE_STATIC) { + ath11k_dbg(ab, ATH11K_DBG_MAC, "ast_entry %pM already present on peer %pM\n", + mac_addr, ast_entry->peer->addr); + return 0; +@@ -298,7 +316,6 @@ int ath11k_peer_update_ast(struct ath11k + ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_update_ast old peer %pM new peer %pM ast_entry %pM\n", + old_peer->addr, peer->addr, ast_entry->addr); + +- flush_work(&ast_entry->wds_wmi_wk); + ast_entry->action = ATH11K_WDS_WMI_UPDATE; + ieee80211_queue_work(ar->hw, &ast_entry->wds_wmi_wk); + +@@ -343,8 +360,8 @@ void ath11k_peer_del_ast(struct ath11k * + + peer = ast_entry->peer; + +- ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_del_ast peer %pM ast_entry %pM\n", +- peer->addr, ast_entry->addr); ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_del_ast pdev:%d peer %pM ast_entry %pM\n", ++ ar->pdev->pdev_id, peer->addr, ast_entry->addr); + + if (ast_entry->is_mapped) + list_del(&ast_entry->ase_list); +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -34,6 +34,7 @@ enum ath11k_ast_entry_type { + enum ath11k_wds_wmi_action { + ATH11K_WDS_WMI_ADD = 1, + ATH11K_WDS_WMI_UPDATE, ++ ATH11K_WDS_WMI_REMOVE, + + ATH11K_WDS_WMI_MAX + }; +@@ -127,6 +128,8 @@ int ath11k_peer_rhash_delete(struct ath1 + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, + u8* addr); ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_pdev_idx(struct ath11k *ar, ++ u8* addr); + int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, + u8* mac_addr, enum ath11k_ast_entry_type type); + int ath11k_peer_update_ast(struct ath11k *ar, struct ath11k_peer *peer, +@@ -146,6 +149,12 @@ static inline struct ath11k_ast_entry *a + { + return NULL; + } ++ ++static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_pdev_idx(struct ath11k *ar, ++ u8* addr) ++{ ++ return NULL; ++} + + static inline int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, + u8* mac_addr, enum ath11k_ast_entry_type type) +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -124,7 +124,10 @@ static void ath11k_nss_get_peer_stats(st + + peer->nss.nss_stats->tx_failed += tx_dropped; + +- ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->vif, tx_bytes, tx_packets); ++ if (peer->nss.ext_vdev_up) ++ ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->nss.ext_vif, tx_bytes, tx_packets); ++ else ++ ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->vif, tx_bytes, tx_packets); + + rx_packets = pstats->rx.rx_recvd; + peer->nss.nss_stats->rx_packets += rx_packets; +@@ -134,7 +137,10 @@ static void ath11k_nss_get_peer_stats(st + pstats->rx.err.decrypt_err; + peer->nss.nss_stats->rx_dropped += rx_dropped; + +- ATH11K_NSS_TXRX_NETDEV_STATS(rx, peer->vif, rx_bytes, rx_packets); ++ if (peer->nss.ext_vdev_up) ++ ATH11K_NSS_TXRX_NETDEV_STATS(tx, peer->nss.ext_vif, rx_bytes, rx_packets); ++ else ++ ATH11K_NSS_TXRX_NETDEV_STATS(rx, peer->vif, rx_bytes, rx_packets); + + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +@@ -997,6 +1003,9 @@ int ath11k_nss_vdev_set_cmd(struct ath11 + case ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD: + cmd = NSS_WIFI_VDEV_DECAP_TYPE_CMD; + break; ++ case ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD: ++ cmd = NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD; ++ break; + default: + return -EINVAL; + } +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -108,6 +108,7 @@ enum ath11k_nss_vdev_cmd { + ATH11K_NSS_WIFI_VDEV_SECURITY_TYPE_CMD, + ATH11K_NSS_WIFI_VDEV_ENCAP_TYPE_CMD, + ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD, ++ ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD, + }; + + enum ath11k_nss_opmode { +@@ -152,6 +153,7 @@ struct ath11k_nss_peer { + uint32_t *vaddr; + dma_addr_t paddr; + bool ext_vdev_up; ++ struct ieee80211_vif *ext_vif; + struct peer_stats *nss_stats; + struct completion complete; + }; diff --git a/package/kernel/mac80211/patches/nss/ath11k/236-002-ath11k-extend-ext-vdev-in-NSS-for-dynamic-VLAN-handling.patch b/package/kernel/mac80211/patches/nss/ath11k/236-002-ath11k-extend-ext-vdev-in-NSS-for-dynamic-VLAN-handling.patch new file mode 100644 index 00000000000000..cb1e39f149e409 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/236-002-ath11k-extend-ext-vdev-in-NSS-for-dynamic-VLAN-handling.patch @@ -0,0 +1,213 @@ +From 6f51430f2614ca2fb2a1e45cc81464c6d7a29f03 Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Fri, 8 Jan 2021 00:34:09 +0530 +Subject: [PATCH 2/3] ath11k: extend ext vdev in NSS for dynamic VLAN handling + +- add ext vdev NSS API callbacks required for dynamic AP_VLAN vif +- existing ext vdev NSS API callbacks are used for both WDS and + dynamic VLAN di_types. + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/nss.c | 101 +++++++++++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/nss.h | 17 ++++++ + 2 files changed, 109 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -1527,14 +1527,11 @@ static int ath11k_nss_ext_vdev_register( + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + nss_tx_status_t status; +- enum nss_dynamic_interface_type di_type; + u32 features = 0; + + if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN || arvif->nss.ctx) + return -EINVAL; + +- di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS; +- + arvif->nss.ctx = nss_wifi_ext_vdev_register_if(arvif->nss.if_num, + ath11k_nss_ext_vdev_data_receive, + ath11k_nss_ext_vdev_special_data_receive, +@@ -1562,7 +1559,8 @@ static void ath11k_nss_ext_vdev_free(str + + status = nss_dynamic_interface_dealloc_node( + arvif->nss.if_num, +- NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS); ++ arvif->nss.di_type); ++ + if (status != NSS_TX_SUCCESS) + ath11k_warn(ab, "failed to free nss ext vdev err:%d\n", + status); +@@ -1571,14 +1569,19 @@ static void ath11k_nss_ext_vdev_free(str + "nss ext vdev interface deallocated\n"); + } + +-static int ath11k_nss_ext_vdev_alloc(struct ath11k_vif *arvif) ++static int ath11k_nss_ext_vdev_alloc(struct ath11k_vif *arvif, ++ struct wireless_dev *wdev) + { + struct ath11k_base *ab = arvif->ar->ab; + enum nss_dynamic_interface_type di_type; + int if_num; + +- di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS; ++ if (wdev->use_4addr) ++ di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_WDS; ++ else ++ di_type = NSS_DYNAMIC_INTERFACE_TYPE_WIFI_EXT_VDEV_VLAN; + ++ arvif->nss.di_type = di_type; + if_num = nss_dynamic_interface_alloc_node(di_type); + if (if_num < 0) { + ath11k_warn(ab, "failed to allocate nss ext vdev\n"); +@@ -1587,8 +1590,8 @@ static int ath11k_nss_ext_vdev_alloc(str + + arvif->nss.if_num = if_num; + ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, +- "nss ext vdev interface %pM allocated if_num %d\n", +- arvif->vif->addr, if_num); ++ "nss ext vdev interface %pM di_type %d allocated if_num %d\n", ++ arvif->vif->addr, di_type, if_num); + + return 0; + } +@@ -1617,7 +1620,7 @@ int ath11k_nss_ext_vdev_create(struct at + return -EINVAL; + } + +- ret = ath11k_nss_ext_vdev_alloc(arvif); ++ ret = ath11k_nss_ext_vdev_alloc(arvif, wdev); + if (ret) + return ret; + +@@ -1730,6 +1733,86 @@ free: + return ret; + } + ++int ath11k_nss_ext_vdev_cfg_dyn_vlan(struct ath11k_vif *arvif, u16 vlan_id) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct nss_wifi_ext_vdev_msg *ext_vdev_msg = NULL; ++ struct nss_wifi_ext_vdev_vlan_msg *cfg_dyn_vlan_msg = NULL; ++ nss_tx_status_t status; ++ int ret; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN) ++ return -EINVAL; ++ ++ ext_vdev_msg = kzalloc(sizeof(struct nss_wifi_ext_vdev_msg), GFP_ATOMIC); ++ if (!ext_vdev_msg) ++ return -ENOMEM; ++ ++ cfg_dyn_vlan_msg = &ext_vdev_msg->msg.vmsg; ++ cfg_dyn_vlan_msg->vlan_id = vlan_id; ++ ++ nss_wifi_ext_vdev_msg_init(ext_vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_EXT_VDEV_MSG_CONFIGURE_VLAN, ++ sizeof(struct nss_wifi_ext_vdev_vlan_msg), ++ NULL, arvif); ++ ++ status = nss_wifi_ext_vdev_tx_msg_sync(arvif->nss.ctx, ext_vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "failed to configure dyn vlan nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(ext_vdev_msg); ++ ++ return ret; ++} ++ ++int ath11k_nss_dyn_vlan_set_group_key(struct ath11k_vif *arvif, u16 vlan_id, ++ u16 group_key) ++{ ++ struct nss_wifi_vdev_msg *vdev_msg = NULL; ++ struct nss_wifi_vdev_set_vlan_group_key *vlan_group_key; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); ++ if (!vdev_msg) ++ return -ENOMEM; ++ ++ vlan_group_key = &vdev_msg->msg.vlan_group_key; ++ vlan_group_key->vlan_id = vlan_id; ++ vlan_group_key->group_key = group_key; ++ ++ nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num, ++ NSS_WIFI_VDEV_SET_GROUP_KEY, ++ sizeof(struct nss_wifi_vdev_set_vlan_group_key), ++ NULL, NULL); ++ ++ status = nss_wifi_vdev_tx_msg(ar->nss.ctx, vdev_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss vdev set vlan group key error %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_WDS, "nss vdev set vlan group key success\n"); ++free: ++ kfree(vdev_msg); ++ return ret; ++ ++} ++ + /*----------------------------Peer Setup/Config -----------------------------*/ + + int ath11k_nss_set_peer_sec_type(struct ath11k *ar, +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -176,6 +176,10 @@ struct arvif_nss { + bool added; + /* Flag to notify if ext vdev is up/down */ + bool ext_vdev_up; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ /* Keep the copy of di_type for nss */ ++ enum nss_dynamic_interface_type di_type; ++#endif + /* WDS cfg should be done only once for ext vdev */ + bool wds_cfg_done; + bool created; +@@ -243,6 +247,9 @@ void ath11k_nss_ext_vdev_unregister(stru + int ath11k_nss_ext_vdev_up(struct ath11k_vif *arvif); + int ath11k_nss_ext_vdev_down(struct ath11k_vif *arvif); + void ath11k_nss_ext_vdev_delete(struct ath11k_vif *arvif); ++int ath11k_nss_ext_vdev_cfg_dyn_vlan(struct ath11k_vif *arvif, u16 vlan_id); ++int ath11k_nss_dyn_vlan_set_group_key(struct ath11k_vif *arvif, u16 vlan_id, ++ u16 group_key); + int ath11k_nss_set_peer_sec_type(struct ath11k *ar, struct ath11k_peer *peer, + struct ieee80211_key_conf *key_conf); + void ath11k_nss_update_sta_stats(struct ath11k_vif *arvif, +@@ -375,6 +382,18 @@ static inline int ath11k_nss_ext_vdev_do + { + return 0; + } ++ ++static inline int ath11k_nss_ext_vdev_cfg_dyn_vlan(struct ath11k_vif *arvif, ++ u16 vlan_id) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_dyn_vlan_set_group_key(struct ath11k_vif *arvif, ++ u16 vlan_id, u16 group_key) ++{ ++ return 0; ++} + + static inline void ath11k_nss_peer_stats_enable(struct ath11k *ar) + { diff --git a/package/kernel/mac80211/patches/nss/ath11k/236-003-ath11k-add-dynamic-VLAN-support-in-NSS-offload.patch b/package/kernel/mac80211/patches/nss/ath11k/236-003-ath11k-add-dynamic-VLAN-support-in-NSS-offload.patch new file mode 100644 index 00000000000000..750fd2b239d548 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/236-003-ath11k-add-dynamic-VLAN-support-in-NSS-offload.patch @@ -0,0 +1,557 @@ +From da432fe6dda831c867416d338def3e277c989287 Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Fri, 8 Jan 2021 00:39:47 +0530 +Subject: [PATCH 3/3] ath11k: add dynamic VLAN support in NSS offload + +Driver should advertise NL80211_EXT_FEATURE_VLAN_OFFLOAD to enable +vlan offload in hostapd. + +Group Key for multiple vlan interfaces are configured with the help +of group key index as NSS uses this index to get the corresponding +group key during transmission. + +Each dynamic AP-VLAN interface choose unique group key index which +will be sent to NSS along with VLAN ID for dynamic VLAN ext vdev +configuration. + +ath11k_mac_op_set_key() does the NSS ext vdev config upon receiving +VLAN ID on group key. + +ath11k_mac_op_sta_state() does the STA assignment from AP vif to +AP_VLAN vif in NSS after mac80211. + +Currently, the firmware supports upto 128 group keys for an AP +interface. The multiple group key support can be enabled during +resource config. + +Co-Developed-by: Seevalamuthu Mariappan +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/core.h | 8 ++ + drivers/net/wireless/ath/ath11k/mac.c | 237 ++++++++++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/wmi.c | 5 + + drivers/net/wireless/ath/ath11k/wmi.h | 2 + + 4 files changed, 233 insertions(+), 19 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -99,6 +99,11 @@ enum ath11k_crypt_mode { + ATH11K_CRYPT_MODE_SW, + }; + ++#define ATH11K_GROUP_KEYS_NUM_MAX 128 ++#define ATH11K_FREE_GROUP_IDX_MAP_BITS (BITS_PER_BYTE * (sizeof(long))) ++#define ATH11K_FREE_GROUP_IDX_MAP_MAX (ATH11K_GROUP_KEYS_NUM_MAX / \ ++ ATH11K_FREE_GROUP_IDX_MAP_BITS) ++ + static inline enum wme_ac ath11k_tid_to_ac(u32 tid) + { + return (((tid == 0) || (tid == 3)) ? WME_AC_BE : +@@ -326,6 +331,20 @@ struct ath11k_mgmt_frame_stats { + u32 tx_compl_fail[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; + }; + ++/** ++ *struct ath11k_dyn_vlan_cfg - dynamic vlan config state info container. ++ * will be used during ieee80211_reconfig ++ * nss offload case ++ *@arvif: driver's data for the corresponding AP_VLAN ieee80211_vif ++ *@sta: ieee80211_sta which is getting associated to AP_VLAN ++ *@cfg_list: list to hold all associated sta's state ++ */ ++struct ath11k_dyn_vlan_cfg { ++ struct ath11k_vif *arvif; ++ struct ieee80211_sta *sta; ++ struct list_head cfg_list; ++}; ++ + struct ath11k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; +@@ -390,6 +409,11 @@ struct ath11k_vif { + struct arvif_nss nss; + #endif + struct list_head ap_vlan_arvifs; ++ /* list required by Dynamic VLAN during fw_recovery */ ++ struct list_head dyn_vlan_cfg; ++ /* VLAN keyidx map required for Dynamic VLAN */ ++ u16 *vlan_keyid_map; ++ DECLARE_BITMAP(free_groupidx_map, ATH11K_GROUP_KEYS_NUM_MAX); + }; + + struct ath11k_vif_iter { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -346,6 +346,10 @@ enum nl80211_he_gi ath11k_mac_he_gi_to_n + return ret; + } + ++static int ath11k_mac_cfg_dyn_vlan(struct ath11k_base *ab, ++ struct ath11k_vif *ap_vlan_arvif, ++ struct ieee80211_sta *sta); ++ + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) + { + u8 ret = 0; +@@ -718,6 +722,33 @@ u8 ath11k_mac_get_target_pdev_id(struct + return ar->ab->target_pdev_ids[0].pdev_id; + } + ++struct ath11k_vif *ath11k_mac_get_ap_arvif_by_addr(struct ath11k_base *ab, ++ const u8 *addr) ++{ ++ int i; ++ struct ath11k_pdev *pdev; ++ struct ath11k_vif *arvif; ++ struct ath11k *ar; ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = rcu_dereference(ab->pdevs_active[i]); ++ if (pdev && pdev->ar) { ++ ar = pdev->ar; ++ ++ spin_lock_bh(&ar->data_lock); ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->vif->type == NL80211_IFTYPE_AP && ++ ether_addr_equal(arvif->vif->addr, addr)) { ++ spin_unlock_bh(&ar->data_lock); ++ return arvif; ++ } ++ } ++ spin_unlock_bh(&ar->data_lock); ++ } ++ } ++ return NULL; ++} ++ + static void ath11k_pdev_caps_update(struct ath11k *ar) + { + struct ath11k_base *ab = ar->ab; +@@ -4167,6 +4198,9 @@ static int ath11k_install_key(struct ath + if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags)) + return 0; + ++ if (key->vlan_id) ++ arg.group_key_idx = key->hw_key_idx; ++ + if (cmd == DISABLE_KEY) { + arg.key_cipher = WMI_CIPHER_NONE; + arg.key_data = NULL; +@@ -4256,15 +4290,40 @@ static int ath11k_clear_peer_keys(struct + return first_errno; + } + ++static int ath11k_get_vlan_groupkey_index(struct ath11k_vif *arvif, ++ struct ieee80211_key_conf *key) ++{ ++ struct ath11k *ar = arvif->ar; ++ int map_idx = 0; ++ int free_bit; ++ ++ for (map_idx = 0; map_idx < ATH11K_FREE_GROUP_IDX_MAP_MAX; map_idx++) ++ if (arvif->free_groupidx_map[map_idx] != 0) ++ break; ++ ++ if (map_idx == ATH11K_FREE_GROUP_IDX_MAP_MAX) ++ return -ENOSPC; ++ ++ spin_lock_bh(&ar->data_lock); ++ /* select the first free key index */ ++ free_bit = __ffs64(arvif->free_groupidx_map[map_idx]); ++ key->hw_key_idx = (map_idx * ATH11K_FREE_GROUP_IDX_MAP_BITS) + free_bit; ++ /* clear the selected bit from free index map */ ++ clear_bit(key->hw_key_idx, arvif->free_groupidx_map); ++ spin_unlock_bh(&ar->data_lock); ++ ++ return 0; ++} ++ + static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) + { + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; +- struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_vif *arvif, *ap_vlan_arvif = NULL; + struct ath11k_peer *peer; +- struct ath11k_sta *arsta; ++ struct ath11k_sta *arsta = NULL; + const u8 *peer_addr; + int ret = 0; + u32 flags = 0; +@@ -4282,17 +4341,38 @@ static int ath11k_mac_op_set_key(struct + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + +- mutex_lock(&ar->conf_mutex); ++ arvif = ath11k_vif_to_arvif(vif); + +- if (sta) ++ mutex_lock(&ar->conf_mutex); ++ if (sta) { + peer_addr = sta->addr; +- else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ } else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { + peer_addr = vif->bss_conf.bssid; +- else ++ } else { + peer_addr = vif->addr; ++ } + + key->hw_key_idx = key->keyidx; + ++ if (ab->nss.enabled && vif->type == NL80211_IFTYPE_AP_VLAN) { ++ ap_vlan_arvif = arvif; ++ if (arsta) { ++ ap_vlan_arvif->nss.ap_vif = arsta->arvif; ++ arvif = arsta->arvif; ++ } else { ++ rcu_read_lock(); ++ arvif = ath11k_mac_get_ap_arvif_by_addr(ab, peer_addr); ++ if (!arvif) { ++ rcu_read_unlock(); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ap_vlan_arvif->nss.ap_vif = arvif; ++ rcu_read_unlock(); ++ } ++ } ++ + /* the peer should not disappear in mid-way (unless FW goes awry) since + * we already hold conf_mutex. we just make sure its there now. + */ +@@ -4337,6 +4417,74 @@ static int ath11k_mac_op_set_key(struct + goto exit; + } + ++ /* VLAN ID is updated non-zero only for AP_VLAN vif */ ++ if (key->vlan_id && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) && ++ ap_vlan_arvif) { ++ if (arvif->vlan_keyid_map) ++ key->hw_key_idx = arvif->vlan_keyid_map[key->vlan_id]; ++ else ++ key->hw_key_idx = 0; ++ switch (cmd) { ++ case SET_KEY: ++ /* If the group key idx is already available, ++ * no need to find the free index again. ++ * This happens during GTK rekey. It uses ++ * the same index after rekey also. ++ */ ++ if (!key->hw_key_idx) ++ ret = ath11k_get_vlan_groupkey_index(arvif, key); ++ break; ++ case DISABLE_KEY: ++ /* If the group key idx is already 0, ++ * no need of freeing the index. ++ */ ++ if (key->hw_key_idx) { ++ spin_lock_bh(&ar->data_lock); ++ /* make the group index as available */ ++ set_bit(key->hw_key_idx, arvif->free_groupidx_map); ++ spin_unlock_bh(&ar->data_lock); ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ if (ret) { ++ ath11k_warn(ab, "failed to set group key index for vlan %u : %d\n", ++ key->vlan_id, ret); ++ goto exit; ++ } ++ ++ ret = ath11k_nss_ext_vdev_configure(ap_vlan_arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to nss cfg ext vdev %pM: %d\n", ++ ap_vlan_arvif->vif->addr, ret); ++ goto exit; ++ } ++ ++ ret = ath11k_nss_ext_vdev_cfg_dyn_vlan(ap_vlan_arvif, ++ key->vlan_id); ++ if (ret) { ++ ath11k_warn(ab, "failed to cfg dynamic vlan %d\n", ret); ++ goto exit; ++ } ++ ++ ret = ath11k_nss_dyn_vlan_set_group_key(ap_vlan_arvif->nss.ap_vif, ++ key->vlan_id, ++ key->hw_key_idx); ++ if (ret) { ++ ath11k_warn(ab, "failed to set dynamic vlan group key %d\n", ++ ret); ++ goto exit; ++ } ++ ++ ret = ath11k_nss_ext_vdev_up(ap_vlan_arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to set dyn vlan UP %d\n", ret); ++ goto exit; ++ } ++ } ++ + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); + +@@ -4359,6 +4507,27 @@ static int ath11k_mac_op_set_key(struct + goto unlock; + } + ++ spin_unlock_bh(&ar->ab->base_lock); ++ if (ap_vlan_arvif && ar->state == ATH11K_STATE_RESTARTED) { ++ /* Keys are getting installed during ieee80211_reconfig(). Configuring ++ * dynamic vlan is pending and the state is saved in the ap_vlan ++ * arvif dyn_vlan_cfg list. We will configure it now since nss peer is authorised */ ++ struct ath11k_dyn_vlan_cfg *dyn_vlan_cfg, *tmp; ++ ++ list_for_each_entry_safe(dyn_vlan_cfg, tmp, &ap_vlan_arvif->dyn_vlan_cfg, cfg_list) { ++ struct ieee80211_sta *vlan_sta = dyn_vlan_cfg->sta; ++ ++ ret = ath11k_mac_cfg_dyn_vlan(ar->ab, ap_vlan_arvif, vlan_sta); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to cfg dyn vlan for peer %pM: %d\n", ++ vlan_sta->addr, ret); ++ /* Configuration is used. Free up space */ ++ list_del(&dyn_vlan_cfg->cfg_list); ++ kfree(dyn_vlan_cfg); ++ } ++ } ++ ++ spin_lock_bh(&ar->ab->base_lock); + if (peer && cmd == SET_KEY) { + peer->keys[key->keyidx] = key; + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { +@@ -4368,18 +4537,23 @@ static int ath11k_mac_op_set_key(struct + peer->mcast_keyidx = key->keyidx; + peer->sec_type_grp = ath11k_dp_tx_get_encrypt_type(key->cipher); + } ++ /* storing group key idx which will be used during rekey */ ++ if (key->vlan_id) ++ arvif->vlan_keyid_map[key->vlan_id] = key->hw_key_idx; + } else if (peer && cmd == DISABLE_KEY) { + peer->keys[key->keyidx] = NULL; + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + peer->ucast_keyidx = 0; + else + peer->mcast_keyidx = 0; +- } else if (!peer) ++ if (key->vlan_id) ++ arvif->vlan_keyid_map[key->vlan_id] = 0; ++ } else if (!peer) { + /* impossible unless FW goes crazy */ + ath11k_warn(ab, "peer %pM disappeared!\n", peer_addr); ++ } + +- if (sta) { +- arsta = ath11k_sta_to_arsta(sta); ++ if (arsta) { + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: +@@ -5186,6 +5360,33 @@ static u32 ath11k_mac_ieee80211_sta_bw_t + return bw; + } + ++static int ath11k_mac_cfg_dyn_vlan(struct ath11k_base *ab, ++ struct ath11k_vif *ap_vlan_arvif, ++ struct ieee80211_sta *sta) ++{ ++ struct ath11k_peer *peer; ++ int peer_id, ret; ++ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_addr(ab, sta->addr); ++ if (!peer) { ++ ath11k_warn(ab, "failed to find peer for %pM\n", sta->addr); ++ spin_unlock_bh(&ab->base_lock); ++ return -EINVAL; ++ } ++ peer_id = peer->peer_id; ++ spin_unlock_bh(&ab->base_lock); ++ ++ ret = ath11k_nss_ext_vdev_wds_4addr_allow(ap_vlan_arvif, peer_id); ++ if (ret) { ++ ath11k_warn(ab, "failed to set 4addr allow for %pM:%d\n", ++ sta->addr, ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -5295,6 +5496,34 @@ static int ath11k_mac_op_sta_state(struc + if (ret) + ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", + sta->addr, arvif->vdev_id, ret); ++ } else if (ar->ab->nss.enabled && ++ vif->type == NL80211_IFTYPE_AP_VLAN && ++ !arsta->use_4addr_set) { ++ ++ if (ar->state == ATH11K_STATE_RESTARTED) { ++ /* During ieee80211_reconfig(), at this point, nss ext vdev peer is not ++ * authorized. Hence ath11k_mac_cfg_dyn_vlan() will return error. We save ++ * the state vars here and use it later once nss ext vdev is authorized ++ * in ath11k_mac_op_set_key() */ ++ struct ath11k_dyn_vlan_cfg *ar_dyn_vlan_cfg; ++ ar_dyn_vlan_cfg = kzalloc(sizeof(*ar_dyn_vlan_cfg), GFP_ATOMIC); ++ ++ if (!ar_dyn_vlan_cfg) { ++ ath11k_warn(ar->ab, "failed to save state for dynamic AP_VLAN configuration (%d)", ++ -ENOSPC); ++ } else { ++ INIT_LIST_HEAD(&ar_dyn_vlan_cfg->cfg_list); ++ ar_dyn_vlan_cfg->arvif = arvif; ++ ar_dyn_vlan_cfg->sta = sta; ++ /* save it to arvif (AP_VLAN) list */ ++ list_add_tail(&ar_dyn_vlan_cfg->cfg_list, &arvif->dyn_vlan_cfg); ++ } ++ } else { ++ ret = ath11k_mac_cfg_dyn_vlan(ar->ab, arvif, sta); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to cfg dyn vlan for peer %pM: %d\n", ++ sta->addr, ret); ++ } + } + } else if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { +@@ -7013,7 +7242,7 @@ static int ath11k_mac_op_add_interface(s + if ((vif->type == NL80211_IFTYPE_AP_VLAN || + vif->type == NL80211_IFTYPE_STATION) && ab->nss.enabled) { + if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET && +- ieee80211_set_hw_80211_encap(vif, true)) { ++ (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)) { + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + arvif->nss.encap = ATH11K_HW_TXRX_ETHERNET; + arvif->nss.decap = ATH11K_HW_TXRX_ETHERNET; +@@ -7026,6 +7255,7 @@ static int ath11k_mac_op_add_interface(s + vif->addr, ret); + goto err; + } ++ INIT_LIST_HEAD(&arvif->dyn_vlan_cfg); + mutex_unlock(&ar->conf_mutex); + return ret; + } +@@ -7050,6 +7280,20 @@ static int ath11k_mac_op_add_interface(s + arvif->vdev_id = bit; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + ++ spin_lock_bh(&ar->data_lock); ++ /* Configure vlan specific parameters */ ++ for (i = 0; i < ATH11K_FREE_GROUP_IDX_MAP_MAX; i++) ++ arvif->free_groupidx_map[i] = 0xFFFFFFFFL; ++ /* Group idx 0 is not valid for VLAN*/ ++ arvif->free_groupidx_map[0] &= ~(1L); ++ spin_unlock_bh(&ar->data_lock); ++ ++ arvif->vlan_keyid_map = kzalloc(4096, GFP_KERNEL); ++ if (!arvif->vlan_keyid_map) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ + switch (vif->type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: +@@ -7090,7 +7334,7 @@ static int ath11k_mac_op_add_interface(s + if (ret) { + ath11k_warn(ab, "failed to create WMI vdev %d: %d\n", + arvif->vdev_id, ret); +- goto err; ++ goto err_keyid; + } + + ar->num_created_vdevs++; +@@ -7249,7 +7493,7 @@ err_peer_del: + if (fbret) { + ath11k_warn(ar->ab, "fallback fail to delete peer addr %pM vdev_id %d ret %d\n", + vif->addr, arvif->vdev_id, fbret); +- goto err; ++ goto err_keyid; + } + } + +@@ -7260,6 +7504,8 @@ err_vdev_del: + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); + ++err_keyid: ++ kfree(arvif->vlan_keyid_map); + err: + mutex_unlock(&ar->conf_mutex); + +@@ -7357,6 +7603,7 @@ err_vdev_del: + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); + ++ kfree(arvif->vlan_keyid_map); + ath11k_peer_cleanup(ar, arvif->vdev_id); + + idr_for_each(&ar->txmgmt_idr, +@@ -9956,8 +10203,11 @@ static int __ath11k_mac_register(struct + ab->hw_params.bios_sar_capa) + ar->hw->wiphy->sar_capa = ab->hw_params.bios_sar_capa; + +- if (ab->nss.enabled) ++ if (ab->nss.enabled) { + ieee80211_hw_set(ar->hw, SUPPORTS_NSS_OFFLOAD); ++ wiphy_ext_feature_set(ar->hw->wiphy, ++ NL80211_EXT_FEATURE_VLAN_OFFLOAD); ++ } + + ret = ieee80211_register_hw(ar->hw); + if (ret) { +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1938,6 +1938,7 @@ int ath11k_wmi_vdev_install_key(struct a + cmd->key_len = arg->key_len; + cmd->key_txmic_len = arg->key_txmic_len; + cmd->key_rxmic_len = arg->key_rxmic_len; ++ cmd->group_key_id = arg->group_key_idx; + + if (arg->key_rsc_counter) + memcpy(&cmd->key_rsc_counter, &arg->key_rsc_counter, +@@ -4213,6 +4214,7 @@ ath11k_wmi_copy_resource_config(struct w + wmi_cfg->flags2 = WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET; + wmi_cfg->ema_max_vap_cnt = tg_cfg->ema_max_vap_cnt; + wmi_cfg->ema_max_profile_period = tg_cfg->ema_max_profile_period; ++ wmi_cfg->max_num_group_keys = tg_cfg->max_num_group_keys; + } + + static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, +@@ -4435,6 +4437,9 @@ int ath11k_wmi_cmd_init(struct ath11k_ba + memset(&init_param, 0, sizeof(init_param)); + memset(&config, 0, sizeof(config)); + ++ if (ab->nss.enabled) ++ config.max_num_group_keys = ATH11K_GROUP_KEYS_NUM_MAX; ++ + ab->hw_params.hw_ops->wmi_init_config(ab, &config); + + if (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -3703,6 +3703,7 @@ struct wmi_vdev_install_key_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; ++ u32 group_key_idx; + u32 key_flags; + u32 key_cipher; + u32 key_len; +@@ -5786,6 +5787,7 @@ struct target_resource_config { + u32 bpf_instruction_size; + u32 max_bssid_rx_filters; + u32 use_pdev_id; ++ u32 max_num_group_keys; + u32 peer_map_unmap_v2_support; + u32 sched_params; + u32 twt_ap_pdev_count; diff --git a/package/kernel/mac80211/patches/nss/ath11k/237-001-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch b/package/kernel/mac80211/patches/nss/ath11k/237-001-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch new file mode 100644 index 00000000000000..0362ff8caa5d0e --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/237-001-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch @@ -0,0 +1,101 @@ +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -1076,6 +1076,7 @@ struct ath11k_base { + u32 max_ast_index; + u32 num_ast_entries; + ++ bool stats_disable; + /* must be last */ + u8 drv_priv[] __aligned(sizeof(void *)); + }; +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -973,6 +973,79 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++static void ath11k_debug_config_mon_status(struct ath11k *ar, bool enable) ++{ ++ struct htt_rx_ring_tlv_filter tlv_filter = {0}; ++ struct ath11k_base *ab = ar->ab; ++ int i; ++ u32 ring_id; ++ ++ if (enable) ++ tlv_filter = ath11k_mac_mon_status_filter_default; ++ ++ for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { ++ ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; ++ ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ++ ar->dp.mac_id + i, ++ HAL_RXDMA_MONITOR_STATUS, ++ DP_RX_BUFFER_SIZE, ++ &tlv_filter); ++ } ++} ++ ++static ssize_t ath11k_write_stats_disable(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_base *ab = file->private_data; ++ struct ath11k_pdev *pdev; ++ bool disable; ++ int ret, i, radioup = 0; ++ u32 mask = 0; ++ ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ if (pdev && pdev->ar) { ++ radioup = 1; ++ break; ++ } ++ } ++ ++ if (radioup == 0) { ++ ath11k_err(ab, "radio is not up\n"); ++ ret = -ENETDOWN; ++ goto exit; ++ } ++ ++ if (kstrtobool_from_user(user_buf, count, &disable)) ++ return -EINVAL; ++ ++ if (disable != ab->stats_disable) { ++ ab->stats_disable = disable; ++ for (i = 0; i < ab->num_radios; i++) { ++ pdev = &ab->pdevs[i]; ++ if (pdev && pdev->ar) { ++ ath11k_debug_config_mon_status(pdev->ar, !disable); ++ ++ if (!disable) ++ mask = HTT_PPDU_STATS_TAG_DEFAULT; ++ ++ ath11k_dp_tx_htt_h2t_ppdu_stats_req(pdev->ar, mask); ++ } ++ } ++ } ++ ++ ret = count; ++ ++exit: ++ return ret; ++} ++ ++static const struct file_operations fops_soc_stats_disable = { ++ .open = simple_open, ++ .write = ath11k_write_stats_disable, ++}; ++ + int ath11k_debugfs_pdev_create(struct ath11k_base *ab) + { + if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) +@@ -1022,6 +1095,8 @@ int ath11k_debugfs_soc_create(struct ath + ret = PTR_ERR(ab->debugfs_soc); + goto out; + } ++ debugfs_create_file("stats_disable", 0600, ab->debugfs_soc, ab, ++ &fops_soc_stats_disable); + + ret = 0; + diff --git a/package/kernel/mac80211/patches/nss/ath11k/237-003-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch b/package/kernel/mac80211/patches/nss/ath11k/237-003-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch new file mode 100644 index 00000000000000..db2bbfa0d2845d --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/237-003-ath11k-allocate-dst-ring-descriptors-from-cacheable-.patch @@ -0,0 +1,147 @@ +From 61342ee83df7fa0b90d5ece88e3f83dea426802c Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 14 Dec 2020 20:22:22 +0530 +Subject: [PATCH] ath11k: allocate dst ring descriptors from cacheable + memory + +tcl_data and reo_dst rings are currently being allocated +using dma_allocate_coherent() which is non cachable. + +Allocating ring memory from cacheable memory area +allows cached descriptor access and prefetch next +descriptors to optimize CPU usage during +descriptor processing on NAPI. + +Signed-off-by: Pradeep Kumar Chitrapu +Signed-off-by: Sriram R +Signed-off-by: P Praneesh +--- + drivers/net/wireless/ath/ath11k/dp.c | 39 ++++++++++++++++++++++++++++++----- + drivers/net/wireless/ath/ath11k/dp.h | 1 + + drivers/net/wireless/ath/ath11k/hal.c | 33 ++++++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/hal.h | 1 + + 4 files changed, 66 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -761,6 +761,7 @@ void ath11k_dp_tx_completion_handler(str + struct sk_buff *msdu; + struct hal_tx_status ts = { 0 }; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; ++ int valid_entries; + u32 *desc; + u32 msdu_id; + u8 mac_id; +@@ -769,9 +770,18 @@ void ath11k_dp_tx_completion_handler(str + + ath11k_hal_srng_access_begin(ab, status_ring); + ++ valid_entries = ath11k_hal_srng_dst_num_free(ab, status_ring, false); ++ if (!valid_entries) { ++ ath11k_hal_srng_access_end(ab, status_ring); ++ spin_unlock_bh(&status_ring->lock); ++ return; ++ } ++ ++ ath11k_hal_srng_dst_invalidate_entry(ab, status_ring, valid_entries); ++ + while ((ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) != + tx_ring->tx_status_tail) && +- (desc = ath11k_hal_srng_dst_get_next_entry(ab, status_ring))) { ++ (desc = ath11k_hal_srng_dst_get_next_cache_entry(ab, status_ring))) { + memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], + desc, sizeof(struct hal_wbm_release_ring)); + tx_ring->tx_status_head = +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -653,7 +653,8 @@ u32 *ath11k_hal_srng_dst_get_next_entry( + + desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; + +- srng->u.dst_ring.tp += srng->entry_size; ++ srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % ++ srng->ring_size; + + /* wrap around to start of ring*/ + if (srng->u.dst_ring.tp == srng->ring_size) +@@ -666,8 +667,63 @@ u32 *ath11k_hal_srng_dst_get_next_entry( + return desc; + } + ++u32 *ath11k_hal_srng_dst_get_next_cache_entry(struct ath11k_base *ab, ++ struct hal_srng *srng) ++{ ++ u32 *desc,*desc_next; ++ lockdep_assert_held(&srng->lock); ++ ++ if (srng->u.dst_ring.tp == srng->u.dst_ring.cached_hp) ++ return NULL; ++ ++ desc = srng->ring_base_vaddr + srng->u.dst_ring.tp; ++ ++ srng->u.dst_ring.tp = (srng->u.dst_ring.tp + srng->entry_size) % ++ srng->ring_size; ++ ++ /* Try to prefetch the next descriptor in the ring */ ++ if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) { ++ /* prefetch only if desc is available */ ++ desc_next = srng->ring_base_vaddr + srng->u.dst_ring.tp; ++ prefetch(desc_next); ++ } ++ return desc; ++} ++ ++void ath11k_hal_srng_dst_invalidate_entry(struct ath11k_base *ab, ++ struct hal_srng *srng, int entries) ++{ ++ u32 *desc; ++ u32 tp, hp; ++ ++ lockdep_assert_held(&srng->lock); ++ ++ if (!(srng->flags & HAL_SRNG_FLAGS_CACHED) || !entries) ++ return; ++ ++ tp = srng->u.dst_ring.tp; ++ hp = srng->u.dst_ring.cached_hp; ++ ++ desc = srng->ring_base_vaddr + tp; ++ if (hp > tp) { ++ dma_sync_single_for_cpu(ab->dev, virt_to_phys(desc), ++ entries * srng->entry_size * sizeof(u32), ++ DMA_FROM_DEVICE); ++ } else { ++ entries = srng->ring_size - tp; ++ dma_sync_single_for_cpu(ab->dev, virt_to_phys(desc), ++ entries * sizeof(u32), ++ DMA_FROM_DEVICE); ++ ++ entries = hp; ++ dma_sync_single_for_cpu(ab->dev, virt_to_phys(srng->ring_base_vaddr), ++ entries * sizeof(u32), ++ DMA_FROM_DEVICE); ++ } ++} ++ + int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, +- bool sync_hw_ptr) ++ bool sync_hw_ptr) + { + u32 tp, hp; + +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -945,8 +945,12 @@ void ath11k_hal_srng_get_params(struct a + u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, + struct hal_srng *srng); + u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng); ++u32 *ath11k_hal_srng_dst_get_next_cache_entry(struct ath11k_base *ab, ++ struct hal_srng *srng); + int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng, +- bool sync_hw_ptr); ++ bool sync_hw_ptr); ++void ath11k_hal_srng_dst_invalidate_entry(struct ath11k_base *ab, ++ struct hal_srng *srng, int entries); + u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng); + u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng); diff --git a/package/kernel/mac80211/patches/nss/ath11k/237-006-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch b/package/kernel/mac80211/patches/nss/ath11k/237-006-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch new file mode 100644 index 00000000000000..d25e43ec0b3998 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/237-006-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch @@ -0,0 +1,345 @@ +From 594992a7ef169aa406e7fc025df2455af5d226be Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Tue, 15 Dec 2020 10:31:30 +0530 +Subject: [PATCH] ath11k: Allow fast rx by bypassing stats update + +Add a provision to disable stats and enable fast rx support +for a peer when it is connected to an AP with ethernet decap support. +All valid IP packets are directly passed to the net core stack +bypassing mac80211 stats update + +Signed-off-by: Sriram R +Signed-off-by: P Praneesh +--- + drivers/net/wireless/ath/ath11k/core.h | 3 ++ + drivers/net/wireless/ath/ath11k/debugfs.c | 76 +++++++++++++++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/dp.c | 45 +++++++++++++++++++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 54 ++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/hw.c | 25 +++++++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 2 + + drivers/net/wireless/ath/ath11k/peer.h | 1 + + 8 files changed, 201 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -143,6 +143,7 @@ struct ath11k_skb_rxcb { + u8 tid; + u16 peer_id; + u16 seq_no; ++ struct napi_struct *napi; + }; + + enum ath11k_hw_rev { +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -340,6 +340,12 @@ static int ath11k_dp_purge_mon_ring(stru + return -ETIMEDOUT; + } + ++static inline u8 ath11k_dp_rx_h_msdu_start_ip_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_get_ip_valid(desc); ++} ++ + /* Returns number of Rx buffers replenished */ + int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, +@@ -1664,7 +1670,7 @@ int ath11k_dp_htt_tlv_iter(struct ath11k + len -= sizeof(*tlv); + + if (tlv_len > len) { +- ath11k_err(ab, "htt tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", ++ ath11k_err(ab, "htt tlv parse failure of tag %hu at byte %zd (%zu bytes left, %hu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } +@@ -2454,10 +2460,60 @@ ath11k_dp_rx_h_find_peer(struct ath11k_b + return peer; + } + ++static bool ath11k_dp_rx_check_fast_rx(struct ath11k *ar, ++ struct sk_buff *msdu, ++ struct hal_rx_desc *rx_desc, ++ struct ath11k_peer *peer) ++{ ++ struct ethhdr *ehdr; ++ struct ath11k_peer *f_peer; ++ struct ath11k_skb_rxcb *rxcb; ++ u8 decap; ++ ++ lockdep_assert_held(&ar->ab->base_lock); ++ ++ decap = ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rx_desc); ++ rxcb = ATH11K_SKB_RXCB(msdu); ++ ++ if (!ar->ab->stats_disable || ++ decap != DP_RX_DECAP_TYPE_ETHERNET2_DIX || ++ peer->vif->type != NL80211_IFTYPE_AP) ++ return false; ++ ++ /* mcbc packets go through mac80211 for PN validation */ ++ if (rxcb->is_mcbc) ++ return false; ++ ++ if (!peer->is_authorized) ++ return false; ++ ++ if (!ath11k_dp_rx_h_msdu_start_ip_valid(ar->ab, rx_desc)) ++ return false; ++ ++ /* fast rx is supported only on ethernet decap, so ++ * we can directly gfet the ethernet header ++ */ ++ ehdr = (struct ethhdr *)msdu->data; ++ ++ /* requires rebroadcast from mac80211 */ ++ if (is_multicast_ether_addr(ehdr->h_dest)) ++ return false; ++ ++ /* check if the msdu needs to be bridged to our connected peer */ ++ f_peer = ath11k_peer_find_by_addr(ar->ab, ehdr->h_dest); ++ ++ if (f_peer && f_peer != peer) ++ return false; ++ ++ /* allow direct rx */ ++ return true; ++} ++ + static void ath11k_dp_rx_h_mpdu(struct ath11k *ar, + struct sk_buff *msdu, + struct hal_rx_desc *rx_desc, +- struct ieee80211_rx_status *rx_status) ++ struct ieee80211_rx_status *rx_status, ++ bool *fast_rx) + { + bool fill_crypto_hdr; + enum hal_encrypt_type enctype; +@@ -2468,9 +2524,13 @@ static void ath11k_dp_rx_h_mpdu(struct a + struct rx_attention *rx_attention; + u32 err_bitmap; + ++ struct wireless_dev *wdev = NULL; ++ struct ath11k_sta *arsta = NULL; ++ + /* PN for multicast packets will be checked in mac80211 */ + rxcb = ATH11K_SKB_RXCB(msdu); +- fill_crypto_hdr = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc); ++ if (!ar->ab->nss.enabled) ++ fill_crypto_hdr = ath11k_dp_rx_h_attn_is_mcbc(ar->ab, rx_desc); + rxcb->is_mcbc = fill_crypto_hdr; + + if (rxcb->is_mcbc) { +@@ -2481,6 +2541,26 @@ static void ath11k_dp_rx_h_mpdu(struct a + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu); + if (peer) { ++ /* If the pkt is a valid IP packet and peer supports ++ * fast rx, deliver directly to net, also note that ++ * pkts with crypto error are not expected to arrive in this ++ * path, so its safe to skip checking errors here */ ++ if (*fast_rx && ++ ath11k_dp_rx_check_fast_rx(ar, msdu, rx_desc, peer)) { ++ wdev = ieee80211_vif_to_wdev(peer->vif); ++ if (wdev) { ++ spin_unlock_bh(&ar->ab->base_lock); ++ ath11k_dp_rx_h_csum_offload(ar, msdu); ++ msdu->dev = wdev->netdev; ++ msdu->protocol = eth_type_trans(msdu, msdu->dev); ++ napi_gro_receive(rxcb->napi, msdu); ++ if (peer->sta) ++ arsta = ++ (struct ath11k_sta *)peer->sta->drv_priv; ++ return; ++ } ++ } ++ + if (rxcb->is_mcbc) + enctype = peer->sec_type_grp; + else +@@ -2490,6 +2570,8 @@ static void ath11k_dp_rx_h_mpdu(struct a + } + spin_unlock_bh(&ar->ab->base_lock); + ++ *fast_rx = false; ++ + rx_attention = ath11k_dp_rx_get_attention(ar->ab, rx_desc); + err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention); + if (enctype != HAL_ENCRYPT_TYPE_OPEN && !err_bitmap) +@@ -2731,7 +2813,8 @@ static void ath11k_dp_rx_deliver_msdu(st + static int ath11k_dp_rx_process_msdu(struct ath11k *ar, + struct sk_buff *msdu, + struct sk_buff_head *msdu_list, +- struct ieee80211_rx_status *rx_status) ++ struct ieee80211_rx_status *rx_status, ++ bool *fast_rx) + { + struct ath11k_base *ab = ar->ab; + struct hal_rx_desc *rx_desc, *lrx_desc; +@@ -2798,8 +2881,13 @@ static int ath11k_dp_rx_process_msdu(str + } + } + ++ ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status, fast_rx); ++ if (*fast_rx) { ++ ab->soc_stats.invalid_rbm++; ++ return 0; ++ } ++ + ath11k_dp_rx_h_ppdu(ar, rx_desc, rx_status); +- ath11k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); + + rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + +@@ -2814,10 +2902,12 @@ static void ath11k_dp_rx_process_receive + struct sk_buff_head *msdu_list, + int mac_id) + { ++ struct ath11k_skb_rxcb *rxcb; + struct sk_buff *msdu; + struct ath11k *ar; + struct ieee80211_rx_status rx_status = {0}; + int ret; ++ bool fast_rx; + + if (skb_queue_empty(msdu_list)) + return; +@@ -2834,7 +2924,12 @@ static void ath11k_dp_rx_process_receive + } + + while ((msdu = __skb_dequeue(msdu_list))) { +- ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); ++ rxcb = ATH11K_SKB_RXCB(msdu); ++ /* Enable fast rx by default, the value will cahnge based on peer cap ++ * and packet type */ ++ fast_rx = true; ++ rxcb->napi = napi; ++ ret = ath11k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status, &fast_rx); + if (unlikely(ret)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "Unable to process msdu %d", ret); +@@ -2842,7 +2937,10 @@ static void ath11k_dp_rx_process_receive + continue; + } + +- ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); ++ /* msdu is already delivered directectly */ ++ if (!fast_rx) ++ ath11k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); ++ + } + } + +@@ -2851,11 +2949,12 @@ void ath11k_dp_rx_from_nss(struct ath11k + { + struct ieee80211_rx_status rx_status = {0}; + struct ath11k_skb_rxcb *rxcb; ++ bool fast_rx = false; + + rxcb = ATH11K_SKB_RXCB(msdu); + + ath11k_dp_rx_h_ppdu(ar, rxcb->rx_desc, &rx_status); +- ath11k_dp_rx_h_mpdu(ar, msdu, rxcb->rx_desc, &rx_status); ++ ath11k_dp_rx_h_mpdu(ar, msdu, rxcb->rx_desc, &rx_status, &fast_rx); + + rx_status.flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + +@@ -4322,6 +4421,7 @@ static int ath11k_dp_rx_h_null_q_desc(st + struct ieee80211_rx_status *status, + struct sk_buff_head *msdu_list) + { ++ bool fast_rx; + u16 msdu_len; + struct hal_rx_desc *desc = (struct hal_rx_desc *)msdu->data; + struct rx_attention *rx_attention; +@@ -4371,7 +4471,8 @@ static int ath11k_dp_rx_h_null_q_desc(st + } + ath11k_dp_rx_h_ppdu(ar, desc, status); + +- ath11k_dp_rx_h_mpdu(ar, msdu, desc, status); ++ fast_rx = false; ++ ath11k_dp_rx_h_mpdu(ar, msdu, desc, status, &fast_rx); + + rxcb->tid = ath11k_dp_rx_h_mpdu_start_tid(ar->ab, desc); + +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -293,6 +293,16 @@ static bool ath11k_hw_ipq8074_rx_desc_ge + __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); + } + ++static u8 ath11k_hw_ipq8074_rx_desc_get_ip_valid(struct hal_rx_desc *desc) ++{ ++ bool ipv4, ipv6; ++ ipv4 = FIELD_GET(RX_MSDU_START_INFO2_IPV4, ++ __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); ++ ipv6 = FIELD_GET(RX_MSDU_START_INFO2_IPV6, ++ __le32_to_cpu(desc->u.ipq8074.msdu_start.info2)); ++ return (ipv4 || ipv6); ++} ++ + static bool ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MPDU_START_INFO1_MPDU_SEQ_CTRL_VALID, +@@ -470,6 +480,16 @@ static bool ath11k_hw_qcn9074_rx_desc_ge + __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); + } + ++static u8 ath11k_hw_qcn9074_rx_desc_get_ip_valid(struct hal_rx_desc *desc) ++{ ++ bool ipv4 , ipv6; ++ ipv4 = FIELD_GET(RX_MSDU_START_INFO2_IPV4, ++ __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); ++ ipv6 = FIELD_GET(RX_MSDU_START_INFO2_IPV6, ++ __le32_to_cpu(desc->u.qcn9074.msdu_start.info2)); ++ return (ipv4 || ipv6); ++} ++ + static bool ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MPDU_START_INFO11_MPDU_SEQ_CTRL_VALID, +@@ -1025,6 +1045,7 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ip_valid = ath11k_hw_ipq8074_rx_desc_get_ip_valid, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, +@@ -1189,6 +1210,7 @@ const struct ath11k_hw_ops ipq5018_ops = + .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ip_valid = ath11k_hw_qcn9074_rx_desc_get_ip_valid, + .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -260,6 +260,7 @@ struct ath11k_hw_ops { + u32 (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc); + u8 (*rx_desc_get_decap_type)(struct hal_rx_desc *desc); + u8 (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc); ++ u8 (*rx_desc_get_ip_valid)(struct hal_rx_desc *desc); + bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc); + bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc); +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5527,6 +5527,14 @@ static int ath11k_mac_op_sta_state(struc + } + } else if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { ++ ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = false; ++ spin_unlock_bh(&ar->ab->base_lock); ++ } else if (old_state == IEEE80211_STA_AUTHORIZED && ++ new_state == IEEE80211_STA_ASSOC) { + spin_lock_bh(&ar->ab->base_lock); + + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); diff --git a/package/kernel/mac80211/patches/nss/ath11k/244-ath11k-dp-tx-perf.patch b/package/kernel/mac80211/patches/nss/ath11k/244-ath11k-dp-tx-perf.patch new file mode 100644 index 00000000000000..c4042611d3d9ea --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/244-ath11k-dp-tx-perf.patch @@ -0,0 +1,633 @@ +From a19b1279d75dd1306c6eac291e985657f988780c Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Thu, 7 Jan 2021 16:32:30 +0530 +Subject: [PATCH] ath11k: dp_tx perf improvements + + Contains below changes, + 1. Add branch prediction in tx path + 2. Allow fast tx completion by freeing skb when stats is disabled. + 3. Remove mod operator overhead for dst ring access to avoid(to be profiled) + 4. Lockless tcl ring usage since rings are selected per cpu + +Sample stats disable command: +echo 1 > /sys/kernel/debug/ath11k/qcn9000\ hw1.0_0000\:01\:00.0/stats_disable +echo 1 > /sys/kernel/debug/ath11k/ipq8074\ hw2.0/stats_disable + +Signed-off-by: Sriram R +Signed-off-by: P Praneesh +--- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/dp.c | 7 +- + drivers/net/wireless/ath/ath11k/dp_tx.c | 118 ++++++++++++++++++-------------- + drivers/net/wireless/ath/ath11k/dp_tx.h | 2 + + drivers/net/wireless/ath/ath11k/hal.c | 9 ++- + drivers/net/wireless/ath/ath11k/mac.c | 9 ++- + 6 files changed, 88 insertions(+), 58 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -115,6 +115,7 @@ static inline enum wme_ac ath11k_tid_to_ + enum ath11k_skb_flags { + ATH11K_SKB_HW_80211_ENCAP = BIT(0), + ATH11K_SKB_CIPHER_SET = BIT(1), ++ ATH11K_SKB_TX_STATUS = BIT(2), + }; + + struct ath11k_skb_cb { +@@ -883,10 +884,13 @@ struct ath11k_dp_ring_bp_stats { + struct ath11k_soc_dp_tx_err_stats { + /* TCL Ring Descriptor unavailable */ + u32 desc_na[DP_TCL_NUM_RING_MAX]; ++ /* TCL Ring IDR unavailable */ ++ u32 idr_na[DP_TCL_NUM_RING_MAX]; + /* Other failures during dp_tx due to mem allocation failure + * idr unavailable etc. + */ + atomic_t misc_fail; ++ atomic_t max_fail; + /* Tx failures due to NSS Tx error status */ + atomic_t nss_tx_fail; + }; +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -358,7 +358,7 @@ void ath11k_dp_stop_shadow_timers(struct + if (!ab->hw_params.supports_shadow_regs) + return; + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + ath11k_dp_shadow_stop_timer(ab, &ab->dp.tx_ring_timer[i]); + + ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); +@@ -373,7 +373,7 @@ static void ath11k_dp_srng_common_cleanu + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); + } +@@ -407,6 +407,11 @@ static int ath11k_dp_srng_common_setup(s + goto err; + } + ++ if (ab->hw_params.max_tx_ring > DP_TCL_NUM_RING_MAX) { ++ srng = &ab->hal.srng_list[dp->tcl_cmd_ring.ring_id]; ++ ath11k_hal_tx_init_data_ring(ab, srng, HAL_TCL_CMD); ++ } ++ + ret = ath11k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS, + 0, 0, DP_TCL_STATUS_RING_SIZE); + if (ret) { +@@ -414,7 +419,7 @@ static int ath11k_dp_srng_common_setup(s + goto err; + } + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + tcl_num = ab->hw_params.hal_params->tcl2wbm_rbm_map[i].tcl_ring_num; + wbm_num = ab->hw_params.hal_params->tcl2wbm_rbm_map[i].wbm_ring_num; + +@@ -437,7 +442,7 @@ static int ath11k_dp_srng_common_setup(s + } + + srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id]; +- ath11k_hal_tx_init_data_ring(ab, srng); ++ ath11k_hal_tx_init_data_ring(ab, srng, HAL_TCL_DATA); + + ath11k_dp_shadow_init_timer(ab, &dp->tx_ring_timer[i], + ATH11K_SHADOW_DP_TIMER_INTERVAL, +@@ -1051,7 +1056,7 @@ void ath11k_dp_free(struct ath11k_base * + + ath11k_dp_reo_cmd_list_cleanup(ab); + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); + idr_for_each(&dp->tx_ring[i].txbuf_idr, + ath11k_dp_tx_pending_cleanup, ab); +@@ -1102,7 +1107,7 @@ int ath11k_dp_alloc(struct ath11k_base * + + size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE; + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + idr_init(&dp->tx_ring[i].txbuf_idr); + spin_lock_init(&dp->tx_ring[i].tx_idr_lock); + dp->tx_ring[i].tcl_data_ring_id = i; +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -134,28 +134,34 @@ int ath11k_dp_tx(struct ath11k *ar, stru + u32 ring_selector = 0; + u8 ring_map = 0; + bool tcl_ring_retry, is_diff_encap = false; +- u8 align_pad, htt_meta_size = 0; +- +- if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))) +- return -ESHUTDOWN; ++ u8 align_pad, htt_meta_size = 0, max_tx_ring, tcl_ring_id, ring_id; + + if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && + !ieee80211_is_data(hdr->frame_control))) + return -ENOTSUPP; + +- pool_id = skb_get_queue_mapping(skb) & (ATH11K_HW_MAX_QUEUES - 1); ++ max_tx_ring = ab->hw_params.max_tx_ring; + +- ring_selector = ab->hw_params.hw_ops->get_ring_selector(skb); ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ if (unlikely(atomic_read(&ab->num_max_allowed) > DP_TX_COMP_MAX_ALLOWED)) { ++ atomic_inc(&ab->soc_stats.tx_err.max_fail); ++ return -ENOSPC; ++ } ++#endif ++ ring_selector = smp_processor_id();; ++ pool_id = ring_selector; + + tcl_ring_sel: + tcl_ring_retry = false; + +- ti.ring_id = ring_selector % ab->hw_params.max_tx_ring; +- ti.rbm_id = ab->hw_params.hal_params->tcl2wbm_rbm_map[ti.ring_id].rbm_id; ++ ring_id = ring_selector % max_tx_ring; ++ tcl_ring_id = (ring_id == DP_TCL_NUM_RING_MAX) ? ++ DP_TCL_NUM_RING_MAX - 1 : ring_id; + +- ring_map |= BIT(ti.ring_id); ++ ring_map |= BIT(ring_id); + +- tx_ring = &dp->tx_ring[ti.ring_id]; ++ ti.buf_id = tcl_ring_id + HAL_RX_BUF_RBM_SW0_BM; ++ tx_ring = &dp->tx_ring[tcl_ring_id]; + + spin_lock_bh(&tx_ring->tx_idr_lock); + ret = idr_alloc(&tx_ring->txbuf_idr, skb, 0, +@@ -163,9 +169,9 @@ tcl_ring_sel: + spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (unlikely(ret < 0)) { +- if (ring_map == (BIT(ab->hw_params.max_tx_ring) - 1) || ++ if (ring_map == (BIT(max_tx_ring) - 1) || + !ab->hw_params.tcl_ring_retry) { +- atomic_inc(&ab->soc_stats.tx_err.misc_fail); ++ ab->soc_stats.tx_err.idr_na[tcl_ring_id]++; + return -ENOSPC; + } + +@@ -276,6 +282,11 @@ tcl_ring_sel: + ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; + } + ++ ti.data_len = skb->len - ti.pkt_offset; ++ skb_cb->pkt_offset = ti.pkt_offset; ++ skb_cb->vif = arvif->vif; ++ skb_cb->ar = ar; ++ + ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, ti.paddr))) { + atomic_inc(&ab->soc_stats.tx_err.misc_fail); +@@ -284,13 +295,13 @@ tcl_ring_sel: + goto fail_remove_idr; + } + +- ti.data_len = skb->len - ti.pkt_offset; +- skb_cb->pkt_offset = ti.pkt_offset; + skb_cb->paddr = ti.paddr; +- skb_cb->vif = arvif->vif; +- skb_cb->ar = ar; + +- hal_ring_id = tx_ring->tcl_data_ring.ring_id; ++ if (ring_id == DP_TCL_NUM_RING_MAX) ++ hal_ring_id = dp->tcl_cmd_ring.ring_id; ++ else ++ hal_ring_id = tx_ring->tcl_data_ring.ring_id; ++ + tcl_ring = &ab->hal.srng_list[hal_ring_id]; + + spin_lock_bh(&tcl_ring->lock); +@@ -303,7 +314,7 @@ tcl_ring_sel: + * desc because the desc is directly enqueued onto hw queue. + */ + ath11k_hal_srng_access_end(ab, tcl_ring); +- ab->soc_stats.tx_err.desc_na[ti.ring_id]++; ++ ab->soc_stats.tx_err.desc_na[tcl_ring_id]++; + spin_unlock_bh(&tcl_ring->lock); + ret = -ENOMEM; + +@@ -312,8 +323,8 @@ tcl_ring_sel: + * checking this ring earlier for each pkt tx. + * Restart ring selection if some rings are not checked yet. + */ +- if (unlikely(ring_map != (BIT(ab->hw_params.max_tx_ring)) - 1) && +- ab->hw_params.tcl_ring_retry && ab->hw_params.max_tx_ring > 1) { ++ if (unlikely(ring_map != (BIT(max_tx_ring)) - 1) && ++ ab->hw_params.tcl_ring_retry && max_tx_ring > 1) { + tcl_ring_retry = true; + ring_selector++; + } +@@ -324,17 +335,17 @@ tcl_ring_sel: + ath11k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc + + sizeof(struct hal_tlv_hdr), &ti); + ++ atomic_inc(&ar->dp.num_tx_pending); ++ atomic_inc(&ab->num_max_allowed); + ath11k_hal_srng_access_end(ab, tcl_ring); + +- ath11k_dp_shadow_start_timer(ab, tcl_ring, &dp->tx_ring_timer[ti.ring_id]); ++ ath11k_dp_shadow_start_timer(ab, tcl_ring, &dp->tx_ring_timer[ti.buf_id]); + + spin_unlock_bh(&tcl_ring->lock); + + ath11k_dbg_dump(ab, ATH11K_DBG_DP_TX, NULL, "dp tx msdu: ", + skb->data, skb->len); + +- atomic_inc(&ar->dp.num_tx_pending); +- atomic_inc(&ab->num_max_allowed); + + return 0; + +@@ -381,7 +392,6 @@ static void ath11k_dp_tx_free_txbuf(stru + ar = ab->pdevs[mac_id].ar; + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); +- atomic_dec(&ab->num_max_allowed); + } + + static void +@@ -395,6 +405,7 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + struct ath11k_skb_cb *skb_cb; + struct ath11k *ar; + struct ath11k_peer *peer; ++ u8 flags = 0; + + spin_lock(&tx_ring->tx_idr_lock); + msdu = idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); +@@ -413,10 +424,30 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); +- atomic_dec(&ab->num_max_allowed); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + ++ flags = skb_cb->flags; ++ ++ /* Free skb here if stats is disabled */ ++ if (ab->stats_disable && !(flags & ATH11K_SKB_TX_STATUS)) { ++ if (msdu->destructor) { ++ msdu->wifi_acked_valid = 1; ++ msdu->wifi_acked = ts->acked; ++ } ++ if (skb_has_frag_list(msdu)) { ++ kfree_skb_list(skb_shinfo(msdu)->frag_list); ++ skb_shinfo(msdu)->frag_list = NULL; ++ } ++ dev_kfree_skb(msdu); ++ return; ++ } ++ ++ if (unlikely(!skb_cb->vif)) { ++ dev_kfree_skb_any(msdu); ++ return; ++ } ++ + if (!skb_cb->vif) { + ieee80211_free_txskb(ar->hw, msdu); + return; +@@ -632,6 +663,7 @@ static void ath11k_dp_tx_complete_msdu(s + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct rate_info rate; ++ u8 flags = 0; + + if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { + /* Must not happen */ +@@ -642,6 +674,20 @@ static void ath11k_dp_tx_complete_msdu(s + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + ++ /* Free skb here if stats is disabled */ ++ if (ab->stats_disable && !(flags & ATH11K_SKB_TX_STATUS)) { ++ if (msdu->destructor) { ++ msdu->wifi_acked_valid = 1; ++ msdu->wifi_acked = ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED; ++ } ++ if (skb_has_frag_list(msdu)) { ++ kfree_skb_list(skb_shinfo(msdu)->frag_list); ++ skb_shinfo(msdu)->frag_list = NULL; ++ } ++ dev_kfree_skb(msdu); ++ return; ++ } ++ + if (unlikely(!rcu_access_pointer(ab->pdevs_active[ar->pdev_idx]))) { + ieee80211_free_txskb(ar->hw, msdu); + return; +@@ -696,7 +742,7 @@ static void ath11k_dp_tx_complete_msdu(s + + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ts->peer_id); +- if (!peer || !peer->sta) { ++ if (unlikely(!peer || !peer->sta)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "dp_tx: failed to find the peer with peer_id %d\n", + ts->peer_id); +@@ -752,19 +798,36 @@ static inline void ath11k_dp_tx_status_p + ts->rate_stats = 0; + } + ++static inline bool ath11k_dp_tx_completion_valid(struct hal_wbm_release_ring *desc) ++{ ++ struct htt_tx_wbm_completion *status_desc; ++ ++ if (FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0) == ++ HAL_WBM_REL_SRC_MODULE_FW) { ++ status_desc = ((struct htt_tx_wbm_completion *)desc) + HTT_TX_WBM_COMP_STATUS_OFFSET; ++ ++ /* Dont consider HTT_TX_COMP_STATUS_MEC_NOTIFY */ ++ if (FIELD_GET(HTT_TX_WBM_COMP_INFO0_STATUS, status_desc->info0) == ++ HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY) ++ return false; ++ } ++ return true; ++} ++ + void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id) + { + struct ath11k *ar; + struct ath11k_dp *dp = &ab->dp; +- int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; ++ int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id, count = 0, i = 0; + struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; + struct sk_buff *msdu; + struct hal_tx_status ts = { 0 }; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; + int valid_entries; + u32 *desc; +- u32 msdu_id; ++ u32 msdu_id, desc_id; + u8 mac_id; ++ struct hal_wbm_release_ring *tx_status; + + spin_lock_bh(&status_ring->lock); + +@@ -779,33 +842,27 @@ void ath11k_dp_tx_completion_handler(str + + ath11k_hal_srng_dst_invalidate_entry(ab, status_ring, valid_entries); + +- while ((ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) != +- tx_ring->tx_status_tail) && +- (desc = ath11k_hal_srng_dst_get_next_cache_entry(ab, status_ring))) { +- memcpy(&tx_ring->tx_status[tx_ring->tx_status_head], +- desc, sizeof(struct hal_wbm_release_ring)); +- tx_ring->tx_status_head = +- ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head); +- } ++ while ((desc = ath11k_hal_srng_dst_get_next_cache_entry(ab, status_ring))) { ++ if (!ath11k_dp_tx_completion_valid((struct hal_wbm_release_ring *)desc)) ++ continue; + +- if (unlikely((ath11k_hal_srng_dst_peek(ab, status_ring) != NULL) && +- (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_head) == +- tx_ring->tx_status_tail))) { +- /* TODO: Process pending tx_status messages when kfifo_is_full() */ +- ath11k_warn(ab, "Unable to process some of the tx_status ring desc because status_fifo is full\n"); ++ memcpy(&tx_ring->tx_status[count], ++ desc, sizeof(struct hal_wbm_release_ring)); ++ count++; + } + + ath11k_hal_srng_access_end(ab, status_ring); + + spin_unlock_bh(&status_ring->lock); + +- while (ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail) != tx_ring->tx_status_head) { +- struct hal_wbm_release_ring *tx_status; +- u32 desc_id; +- +- tx_ring->tx_status_tail = +- ATH11K_TX_COMPL_NEXT(tx_ring->tx_status_tail); +- tx_status = &tx_ring->tx_status[tx_ring->tx_status_tail]; ++ if (atomic_sub_return(count, &ab->num_max_allowed) < 0) { ++ ath11k_warn(ab, "tx completion mismatch count %d ring id %d max_num %d\n", ++ count, tx_ring->tcl_data_ring_id, ++ atomic_read(&ab->num_max_allowed)); ++ } ++ ++ while (count--) { ++ tx_status = &tx_ring->tx_status[i++]; + ath11k_dp_tx_status_parse(ab, tx_status, &ts); + + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, +@@ -838,7 +895,6 @@ void ath11k_dp_tx_completion_handler(str + wake_up(&ar->dp.tx_empty_waitq); + + ath11k_dp_tx_complete_msdu(ar, msdu, &ts); +- atomic_dec(&ab->num_max_allowed); + } + } + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6664,12 +6664,22 @@ static void ath11k_mac_op_tx(struct ieee + if (control->sta) + arsta = ath11k_sta_to_arsta(control->sta); + ++ /* Must call mac80211 tx status handler, else when stats is disabled we free ++ * the skb from driver. Own tx packets on monitor will also be disabled. ++ */ ++ if ((info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_INTFL_NL80211_FRAME_TX)) || ++ info->ack_frame_id || vif->type == NL80211_IFTYPE_MESH_POINT || ++ test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) ++ skb_cb->flags |= ATH11K_SKB_TX_STATUS; ++ + if (ar->ab->nss.enabled) + ret = ath11k_nss_tx(arvif, skb); + else + ret = ath11k_dp_tx(ar, arvif, arsta, skb); ++ + if (unlikely(ret)) { +- ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); ++ if (!ar->ab->nss.enabled && ret != -ENOSPC && ret != -ENOMEM) ++ ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->hw, skb); + return; + } +@@ -7617,7 +7627,7 @@ err_vdev_del: + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_vif_txmgmt_idr_remove, vif); + +- for (i = 0; i < ab->hw_params.max_tx_ring; i++) { ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); + idr_for_each(&ab->dp.tx_ring[i].txbuf_idr, + ath11k_mac_vif_unref, vif); +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -832,10 +832,22 @@ static ssize_t ath11k_debugfs_dump_soc_d + len += scnprintf(buf + len, size - len, "ring%d: %u\n", + i, soc_stats->tx_err.desc_na[i]); + ++ len += scnprintf(buf + len, size - len, "\nTCL Ring idr Failures:\n"); ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) ++ len += scnprintf(buf + len, size - len, "ring%d: %u\n", ++ i, soc_stats->tx_err.idr_na[i]); ++ ++ len += scnprintf(buf + len, size - len, "\nMax Transmit Failures: %d\n", ++ atomic_read(&soc_stats->tx_err.max_fail)); ++ + len += scnprintf(buf + len, size - len, + "\nMisc Transmit Failures: %d\n", + atomic_read(&soc_stats->tx_err.misc_fail)); + ++ len += scnprintf(buf + len, size - len, ++ "\nNSS Transmit Failures: %d\n", ++ atomic_read(&soc_stats->tx_err.nss_tx_fail)); ++ + len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len); + + if (len > size) +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -188,7 +188,6 @@ static struct ath11k_hw_params ath11k_hw + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, +- .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = true, +@@ -213,6 +212,8 @@ static struct ath11k_hw_params ath11k_hw + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, ++ /* In addition to TCL ring use TCL_CMD ring also for tx */ ++ .max_tx_ring = DP_TCL_NUM_RING_MAX + 1, + }, + { + .name = "qca6390 hw2.0", +@@ -355,7 +356,6 @@ static struct ath11k_hw_params ath11k_hw + .supports_regdb = false, + .fix_l1ss = true, + .credit_flow = false, +- .max_tx_ring = DP_TCL_NUM_RING_MAX, + .hal_params = &ath11k_hw_hal_params_ipq8074, + .supports_dynamic_smps_6ghz = true, + .alloc_cacheable_memory = true, +@@ -380,6 +380,8 @@ static struct ath11k_hw_params ath11k_hw + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, ++ /* In addition to TCL ring use TCL_CMD ring also for tx */ ++ .max_tx_ring = DP_TCL_NUM_RING_MAX + 1, + }, + { + .name = "wcn6855 hw2.0", +@@ -2143,6 +2145,9 @@ int ath11k_core_pre_init(struct ath11k_b + if (nss_offload) + ab->nss.stats_enabled = 1; + ++ if (ab->nss.enabled && ab->hw_params.max_tx_ring > DP_TCL_NUM_RING_MAX) ++ ab->hw_params.max_tx_ring = DP_TCL_NUM_RING_MAX; ++ + return 0; + } + EXPORT_SYMBOL(ath11k_core_pre_init); +--- a/drivers/net/wireless/ath/ath11k/hal_tx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_tx.h +@@ -18,7 +18,7 @@ + + struct hal_tx_info { + u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */ +- u8 ring_id; ++ u8 buf_id; + u32 desc_id; + enum hal_tcl_desc_type type; + enum hal_tcl_encap_type encap_type; +@@ -70,5 +70,5 @@ int ath11k_hal_reo_cmd_send(struct ath11 + enum hal_reo_cmd_type type, + struct ath11k_hal_reo_cmd *cmd); + void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, +- struct hal_srng *srng); ++ struct hal_srng *srng, enum hal_ring_type type); + #endif +--- a/drivers/net/wireless/ath/ath11k/hal_tx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_tx.c +@@ -37,18 +37,18 @@ static const u8 dscp_tid_map[DSCP_TID_MA + void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd, + struct hal_tx_info *ti) + { +- struct hal_tcl_data_cmd *tcl_cmd = cmd; ++ struct hal_tcl_data_cmd tcl_cmd, *tcl_desc = cmd; + +- tcl_cmd->buf_addr_info.info0 = ++ tcl_cmd.buf_addr_info.info0 = + FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, ti->paddr); +- tcl_cmd->buf_addr_info.info1 = ++ tcl_cmd.buf_addr_info.info1 = + FIELD_PREP(BUFFER_ADDR_INFO1_ADDR, + ((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT)); +- tcl_cmd->buf_addr_info.info1 |= +- FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, ti->rbm_id) | ++ tcl_cmd.buf_addr_info.info1 |= ++ FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, ti->buf_id) | + FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, ti->desc_id); + +- tcl_cmd->info0 = ++ tcl_cmd.info0 = + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_DESC_TYPE, ti->type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE, +@@ -60,24 +60,26 @@ void ath11k_hal_tx_cmd_desc_setup(struct + FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM, + ti->meta_data_flags); + +- tcl_cmd->info1 = ti->flags0 | ++ tcl_cmd.info1 = ti->flags0 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_DATA_LEN, ti->data_len) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset); + +- tcl_cmd->info2 = ti->flags1 | ++ tcl_cmd.info2 = ti->flags1 | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id); + +- tcl_cmd->info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, ++ tcl_cmd.info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, + ti->dscp_tid_tbl_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX, + ti->bss_ast_idx) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM, + ti->bss_ast_hash); +- tcl_cmd->info4 = 0; ++ tcl_cmd.info4 = 0; + + if (ti->enable_mesh) +- ab->hw_params.hw_ops->tx_mesh_enable(ab, tcl_cmd); ++ ab->hw_params.hw_ops->tx_mesh_enable(ab, &tcl_cmd); ++ ++ *tcl_desc = tcl_cmd; + } + + void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id) +@@ -137,7 +139,9 @@ void ath11k_hal_tx_set_dscp_tid_map(stru + ctrl_reg_val); + } + +-void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng) ++void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng, ++ enum hal_ring_type type) ++ + { + struct hal_srng_params params; + struct hal_tlv_hdr *tlv; +@@ -146,7 +150,7 @@ void ath11k_hal_tx_init_data_ring(struct + + memset(¶ms, 0, sizeof(params)); + +- entry_size = ath11k_hal_srng_get_entrysize(ab, HAL_TCL_DATA); ++ entry_size = ath11k_hal_srng_get_entrysize(ab, type); + ath11k_hal_srng_get_params(ab, srng, ¶ms); + desc = (u8 *)params.ring_base_vaddr; + diff --git a/package/kernel/mac80211/patches/nss/ath11k/270-iphone-issue.patch b/package/kernel/mac80211/patches/nss/ath11k/270-iphone-issue.patch new file mode 100644 index 00000000000000..717c11bdfe39e1 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/270-iphone-issue.patch @@ -0,0 +1,11 @@ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6262,6 +6262,8 @@ static int ath11k_mac_copy_he_cap(struct + memcpy(he_cap_elem->phy_cap_info, band_cap->he_cap_phy_info, + sizeof(he_cap_elem->phy_cap_info)); + ++ he_cap_elem->mac_cap_info[0] &= ~IEEE80211_HE_MAC_CAP0_HTC_HE; ++ + he_cap_elem->mac_cap_info[1] &= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK; + diff --git a/package/kernel/mac80211/patches/nss/ath11k/300-ath11k-nss-mesh-offload-support.patch b/package/kernel/mac80211/patches/nss/ath11k/300-ath11k-nss-mesh-offload-support.patch new file mode 100644 index 00000000000000..30848b8f6164ac --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/300-ath11k-nss-mesh-offload-support.patch @@ -0,0 +1,3365 @@ +From fbe5a76d8c9ff1cf3f906a3c863928fc1adcbc95 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Tue, 16 Feb 2021 13:44:39 +0530 +Subject: [PATCH] ath11k: Add mesh nss offload support + +- New capability advertising nss offload support for mesh type +- Mesh obj vap and link vap registration/clean up +- Command/event handling +- New .ch files in ath11k for nss mesh offload related debugs +- Tx/Rx data path on mesh link vap uses native wifi format +- Mesh obj vap handls packets in ether format. No Tx on Mesh + obj vap is expected as packets transmitted in slow path is + supposed to be encapsulated in 802.11 format. +- New mac80211-driver callbacks for mesh vap, mpath and mpp + configurations. + +Signed-off-by: Vasanthakumar Thiagarajan + +Change-Id: Ib6950344286ba18fab43586262c62dcd09557614 +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Vasanthakumar Thiagarajan +--- + drivers/net/wireless/ath/ath11k/Makefile | 2 +- + drivers/net/wireless/ath/ath11k/debug_nss.c | 343 +++++++ + drivers/net/wireless/ath/ath11k/debug_nss.h | 24 + + drivers/net/wireless/ath/ath11k/dp.h | 16 +- + drivers/net/wireless/ath/ath11k/dp_rx.c | 170 ++- + drivers/net/wireless/ath/ath11k/dp_rx.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 36 +- + drivers/net/wireless/ath/ath11k/nss.c | 1482 ++++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/nss.h | 63 +- + 19 files changed, 2548 insertions(+), 206 deletions(-) + create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.c + create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.h + +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -27,6 +27,10 @@ ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spec + ath11k-$(CONFIG_PM) += wow.o + ath11k-$(CPTCFG_ATH11K_NSS_SUPPORT) += nss.o + ++ifeq ($(and $(CPTCFG_ATH11K_DEBUGFS),$(CPTCFG_ATH11K_NSS_MESH_SUPPORT)),y) ++ath11k-y += debug_nss.o ++endif ++ + obj-$(CPTCFG_ATH11K_AHB) += ath11k_ahb.o + ath11k_ahb-y += ahb.o + +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/debug_nss.c +@@ -0,0 +1,924 @@ ++// SPDX-License-Identifier: BSD-3-Clause-Clear ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. ++ */ ++ ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ ++#include ++#include "dp_rx.h" ++#include "nss.h" ++#include "debug.h" ++#include "debug_nss.h" ++ ++static unsigned int ++debug_nss_fill_mpp_dump(struct ath11k_vif *arvif, char *buf, ssize_t size) ++{ ++ struct arvif_nss *nss = &arvif->nss; ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_nss_mpp_entry *entry, *tmp; ++ LIST_HEAD(local_entry); ++ unsigned int len = 0; ++ int i; ++ ++ len += scnprintf(buf + len, size - len, "\nProxy path table\n"); ++ len += scnprintf(buf + len, size - len, "dest_mac_addr\t\tmesh_dest_mac\t\tflags\n"); ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpp_dump, &local_entry); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(entry, tmp, &local_entry, list) { ++ for (i = 0; i < entry->num_entries; i++) ++ len += scnprintf(buf + len, size - len, "%pM\t%pM\t0x%x\n", ++ entry->mpp[i].dest_mac_addr, ++ entry->mpp[i].mesh_dest_mac, entry->mpp[i].flags); ++ list_del(&entry->list); ++ kfree(entry); ++ } ++ ++ return len; ++} ++ ++static int ath11k_nss_dump_mpp_open(struct inode *inode, struct file *file) ++{ ++ struct ath11k_vif *arvif = inode->i_private; ++ struct ath11k *ar = arvif->ar; ++ unsigned long time_left; ++ struct ath11k_nss_dbg_priv_data *priv_data; ++ int ret; ++ ssize_t size = 100; ++ char *buf; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ reinit_completion(&arvif->nss.dump_mpp_complete); ++ ++ priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); ++ if (!priv_data) { ++ mutex_unlock(&ar->conf_mutex); ++ return -ENOMEM; ++ } ++ ++ priv_data->arvif = arvif; ++ ret = ath11k_nss_dump_mpp_request(arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send dump mpp command %d\n", ret); ++ goto err_unlock; ++ } ++ ++ time_left = wait_for_completion_timeout(&arvif->nss.dump_mpp_complete, ++ ATH11K_NSS_MPATH_DUMP_TIMEOUT); ++ if (time_left == 0) { ++ ret = -ETIMEDOUT; ++ goto err_unlock; ++ } ++ ++ mutex_unlock(&ar->conf_mutex); ++ ++ size += (arvif->nss.mpp_dump_num_entries * 200 + 10 * 100); ++ buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ priv_data->buf = buf; ++ ++ priv_data->len= debug_nss_fill_mpp_dump(arvif, buf, size); ++ ++ file->private_data = priv_data; ++ ++ return 0; ++ ++err_unlock: ++ kfree(priv_data); ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static int ath11k_nss_dump_mpp_release(struct inode *inode, struct file *file) ++{ ++ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data; ++ ++ kfree(priv_data->buf); ++ kfree(priv_data); ++ return 0; ++} ++ ++static ssize_t ath11k_nss_dump_mpp_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data; ++ struct ath11k_vif *arvif = priv_data->arvif; ++ char *buf = priv_data->buf; ++ struct ath11k *ar = arvif->ar; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, priv_data->len); ++ ++ mutex_unlock(&ar->conf_mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations fops_nss_dump_mpp_table = { ++ .open = ath11k_nss_dump_mpp_open, ++ .read = ath11k_nss_dump_mpp_read, ++ .release = ath11k_nss_dump_mpp_release, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static unsigned int ++debug_nss_fill_mpath_dump(struct ath11k_vif *arvif, char *buf, ssize_t size) ++{ ++ struct arvif_nss *nss = &arvif->nss; ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_nss_mpath_entry *entry, *tmp; ++ LIST_HEAD(local_entry); ++ unsigned int len = 0; ++ u64 expiry_time; ++ int i; ++ ++ len += scnprintf(buf + len, size - len, "\nmpath table\n"); ++ len += scnprintf(buf + len, size - len, "dest_mac_addr\t\tnext_hop_mac\t\tmetric\t" ++ "expiry_time\thop_count\tflags\tlink_vap_id\n"); ++ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpath_dump, &local_entry); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(entry, tmp, &local_entry, list) { ++ for (i = 0; i < entry->num_entries; i++) { ++ memcpy(&expiry_time, entry->mpath[i].expiry_time, sizeof(u64)); ++ len += scnprintf(buf + len, size - len, "%pM\t%pM\t%u\t%llu\t\t\t%d\t0x%x\t%d\n", ++ entry->mpath[i].dest_mac_addr, ++ entry->mpath[i].next_hop_mac_addr, entry->mpath[i].metric, ++ expiry_time, entry->mpath[i].hop_count, ++ entry->mpath[i].flags, entry->mpath[i].link_vap_id); ++ } ++ kfree(entry); ++ } ++ ++ return len; ++} ++ ++static int ath11k_nss_dump_mpath_open(struct inode *inode, struct file *file) ++{ ++ struct ath11k_vif *arvif = inode->i_private; ++ struct ath11k *ar = arvif->ar; ++ unsigned long time_left; ++ struct ath11k_nss_dbg_priv_data *priv_data; ++ ssize_t size = 200; ++ char *buf; ++ int ret; ++ ++ reinit_completion(&arvif->nss.dump_mpath_complete); ++ ++ priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); ++ if (!priv_data) ++ return -ENOMEM; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ priv_data->arvif = arvif; ++ ret = ath11k_nss_dump_mpath_request(arvif); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to send dump mpath command %d\n", ret); ++ goto err_unlock; ++ } ++ ++ time_left = wait_for_completion_timeout(&arvif->nss.dump_mpath_complete, ++ ATH11K_NSS_MPATH_DUMP_TIMEOUT); ++ if (time_left == 0) { ++ ret = -ETIMEDOUT; ++ goto err_unlock; ++ } ++ ++ mutex_unlock(&ar->conf_mutex); ++ ++ size += (arvif->nss.mpath_dump_num_entries * 200 + 10 * 100); ++ buf = kmalloc(size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ priv_data->buf = buf; ++ ++ priv_data->len = debug_nss_fill_mpath_dump(arvif, buf, size); ++ ++ file->private_data = priv_data; ++ ++ return 0; ++ ++err_unlock: ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ ++static int ath11k_nss_dump_mpath_release(struct inode *inode, struct file *file) ++{ ++ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data; ++ ++ kfree(priv_data->buf); ++ kfree(priv_data); ++ return 0; ++ ++} ++ ++static ssize_t ath11k_nss_dump_mpath_read(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_nss_dbg_priv_data *priv_data = file->private_data; ++ struct ath11k_vif *arvif = priv_data->arvif; ++ char *buf = priv_data->buf; ++ struct ath11k *ar = arvif->ar; ++ int ret; ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, priv_data->len); ++ ++ mutex_unlock(&ar->conf_mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations fops_nss_dump_mpath_table = { ++ .open = ath11k_nss_dump_mpath_open, ++ .read = ath11k_nss_dump_mpath_read, ++ .release = ath11k_nss_dump_mpath_release, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_mpath_add(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%u %hhu %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu %hhu", ++ &path.metric, ++ &path.hop_count, ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5], ++ &path.next_hop[0], ++ &path.next_hop[1], ++ &path.next_hop[2], ++ &path.next_hop[3], ++ &path.next_hop[4], ++ &path.next_hop[5], ++ &path.block_mesh_fwd, ++ &path.metadata_type); ++ ++ ++ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED; ++ ++ if (ret != 16) ++ return -EINVAL; ++ ++ /* Configure the mpath */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpath_add = { ++ .open = simple_open, ++ .write = ath11k_nss_mpath_add, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_mpp_add(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ if (!arvif->ar->ab->nss.debug_mode) { ++ ret = -EPERM; ++ return ret; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", ++ &path.da[0], ++ &path.da[1], ++ &path.da[2], ++ &path.da[3], ++ &path.da[4], ++ &path.da[5], ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5]); ++ ++ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED; ++ ++ if (ret != 12) ++ return -EINVAL; ++ ++ /* Configure the mpp */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpp_add = { ++ .open = simple_open, ++ .write = ath11k_nss_mpp_add, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_mpath_update(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%u %hhu %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu %lu %hhu %hhu", ++ &path.metric, ++ &path.hop_count, ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5], ++ &path.next_hop[0], ++ &path.next_hop[1], ++ &path.next_hop[2], ++ &path.next_hop[3], ++ &path.next_hop[4], ++ &path.next_hop[5], ++ &path.old_next_hop[0], ++ &path.old_next_hop[1], ++ &path.old_next_hop[2], ++ &path.old_next_hop[3], ++ &path.old_next_hop[4], ++ &path.old_next_hop[5], ++ &path.mesh_gate, ++ &path.exp_time, ++ &path.block_mesh_fwd, ++ &path.metadata_type); ++ ++ ++ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED; ++ ++ if (ret != 24) ++ return -EINVAL; ++ ++ /* Configure the mpath */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpath_update = { ++ .open = simple_open, ++ .write = ath11k_nss_mpath_update, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_mpp_update(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ if (!arvif->ar->ab->nss.debug_mode) { ++ ret = -EPERM; ++ return ret; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", ++ &path.da[0], ++ &path.da[1], ++ &path.da[2], ++ &path.da[3], ++ &path.da[4], ++ &path.da[5], ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5]); ++ ++ path.flags |= IEEE80211_MESH_PATH_ACTIVE | IEEE80211_MESH_PATH_RESOLVED; ++ ++ if (ret != 12) ++ return -EINVAL; ++ ++ /* Configure the mpp */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpp_update = { ++ .open = simple_open, ++ .write = ath11k_nss_mpp_update, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t ath11k_nss_mpath_delete(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ if (!arvif->ar->ab->nss.debug_mode) { ++ ret = -EPERM; ++ return ret; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5], ++ &path.next_hop[0], ++ &path.next_hop[1], ++ &path.next_hop[2], ++ &path.next_hop[3], ++ &path.next_hop[4], ++ &path.next_hop[5]); ++ ++ path.flags |= IEEE80211_MESH_PATH_DELETED; ++ ++ if (ret != 12) ++ return -EINVAL; ++ ++ /* Configure the mpath */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpath ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpath_del = { ++ .open = simple_open, ++ .write = ath11k_nss_mpath_delete, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_mpp_delete(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ieee80211_mesh_path_offld path = {0}; ++ u8 buf[128] = {0}; ++ int ret; ++ ++ if (!arvif->ar->ab->nss.debug_mode) { ++ ret = -EPERM; ++ return ret; ++ } ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", ++ &path.da[0], ++ &path.da[1], ++ &path.da[2], ++ &path.da[3], ++ &path.da[4], ++ &path.da[5], ++ &path.mesh_da[0], ++ &path.mesh_da[1], ++ &path.mesh_da[2], ++ &path.mesh_da[3], ++ &path.mesh_da[4], ++ &path.mesh_da[5]); ++ ++ path.flags |= IEEE80211_MESH_PATH_DELETED; ++ ++ if (ret != 12) ++ return -EINVAL; ++ ++ /* Configure the mpp */ ++ ret = ath11k_nss_mesh_config_path(arvif->ar, arvif, ++ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP, ++ &path); ++ if(ret) { ++ ath11k_warn(arvif->ar->ab, "failed to configure mpp ret %d\n", ret); ++ return -EINVAL; ++ } ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_mpp_del = { ++ .open = simple_open, ++ .write = ath11k_nss_mpp_delete, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t ath11k_nss_assoc_link(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ u8 buf[128] = {0}; ++ int ret; ++ u32 assoc_link = 0; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%u", &assoc_link); ++ ++ if (ret != 1) ++ return -EINVAL; ++ ++ arvif->ar->ab->nss.debug_mode = true; ++ arvif->vif->driver_flags |= IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE; ++ ++ ret = ath11k_nss_assoc_link_arvif_to_ifnum(arvif, assoc_link); ++ ++ return ret ? ret : count; ++ ++} ++ ++static const struct file_operations fops_nss_assoc_link = { ++ .open = simple_open, ++ .write = ath11k_nss_assoc_link, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_links(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ char buf[512] = {0}; ++ struct arvif_nss *nss; ++ int len = 0; ++ ++ list_for_each_entry(nss, &mesh_vaps, list) ++ len += scnprintf(buf + len, sizeof(buf) - len, "link id %d\n", ++ nss->if_num); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, len); ++} ++ ++static const struct file_operations fops_nss_links = { ++ .open = simple_open, ++ .read = ath11k_nss_links, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_vap_link_id(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct arvif_nss *nss = &arvif->nss; ++ char buf[512] = {0}; ++ int len = 0; ++ ++ len = scnprintf(buf, sizeof(buf) - len, "link id %d\n", ++ nss->if_num); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, len); ++} ++ ++static const struct file_operations fops_nss_vap_link_id = { ++ .open = simple_open, ++ .read = ath11k_nss_vap_link_id, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_read_mpp_mode(struct file *file, ++ char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ char buf[512] = {0}; ++ int len = 0; ++ ++ len = scnprintf(buf, sizeof(buf) - len, "%s\n",mpp_mode ? ++ "Host Assisted Learning" : "NSS Independent Learning"); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, len); ++} ++ ++static ssize_t ath11k_nss_write_mpp_mode(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ u8 buf[128] = {0}; ++ int ret; ++ u32 mppath_mode = 0; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ret = sscanf(buf, "%u", &mppath_mode); ++ ++ if (ret != 1) ++ return -EINVAL; ++ ++ mpp_mode = mppath_mode; ++ ++ ret = 0; ++ ++ return ret ? ret : count; ++} ++ ++static const struct file_operations fops_nss_mpp_mode = { ++ .open = simple_open, ++ .write = ath11k_nss_write_mpp_mode, ++ .read = ath11k_nss_read_mpp_mode, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_write_excep_flags(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ u8 buf[128] = {0}; ++ int ret; ++ struct nss_wifi_mesh_exception_flag_msg msg; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ++ ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %hhu", ++ &msg.dest_mac_addr[0], ++ &msg.dest_mac_addr[1], ++ &msg.dest_mac_addr[2], ++ &msg.dest_mac_addr[3], ++ &msg.dest_mac_addr[4], ++ &msg.dest_mac_addr[5], ++ &msg.exception); ++ ++ if (ret != 7) ++ return -EINVAL; ++ ++ ret = ath11k_nss_mesh_exception_flags(arvif, &msg); ++ ++ return ret ? ret : count; ++} ++ ++static const struct file_operations fops_nss_excep_flags = { ++ .open = simple_open, ++ .write = ath11k_nss_write_excep_flags, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_write_metadata_type(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ struct ath11k *ar = arvif->ar; ++ u8 buf[128] = {0}; ++ int ret; ++ u8 pkt_type; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ++ ret = sscanf(buf, "%hhu", &pkt_type); ++ mutex_lock(&ar->conf_mutex); ++ arvif->nss.metadata_type = pkt_type ? NSS_WIFI_MESH_PRE_HEADER_80211 : NSS_WIFI_MESH_PRE_HEADER_NONE; ++ mutex_unlock(&ar->conf_mutex); ++ ++ if (ret != 1) ++ return -EINVAL; ++ ++ ret = 0; ++ ++ return ret ? ret : count; ++} ++ ++static const struct file_operations fops_nss_metadata_type = { ++ .open = simple_open, ++ .write = ath11k_nss_write_metadata_type, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t ath11k_nss_write_exc_rate_limit(struct file *file, ++ const char __user *user_buf, ++ size_t count, loff_t *ppos) ++{ ++ struct ath11k_vif *arvif = file->private_data; ++ u8 buf[128] = {0}; ++ int ret; ++ struct nss_wifi_mesh_rate_limit_config nss_exc_cfg; ++ ++ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); ++ if (ret < 0) ++ return ret; ++ ++ buf[ret] = '\0'; ++ ++ ret = sscanf(buf, "%u %u %u", ++ &nss_exc_cfg.exception_num, ++ &nss_exc_cfg.enable, ++ &nss_exc_cfg.rate_limit); ++ ++ if (ret != 3) ++ return -EINVAL; ++ ++ ret = ath11k_nss_exc_rate_config(arvif, &nss_exc_cfg); ++ ++ return ret ? ret : count; ++} ++ ++static const struct file_operations fops_nss_exc_rate_limit = { ++ .open = simple_open, ++ .write = ath11k_nss_write_exc_rate_limit, ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++}; ++ ++void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif) ++{ ++ struct dentry *debugfs_nss_mesh_dir, *debugfs_dbg_infra; ++ ++ debugfs_nss_mesh_dir = debugfs_create_dir("nss_mesh", arvif->vif->debugfs_dir); ++ debugfs_dbg_infra = debugfs_create_dir("dbg_infra", debugfs_nss_mesh_dir); ++ ++ debugfs_create_file("dump_nss_mpath_table", 0600, ++ debugfs_nss_mesh_dir, arvif, ++ &fops_nss_dump_mpath_table); ++ ++ debugfs_create_file("dump_nss_mpp_table", 0600, ++ debugfs_nss_mesh_dir, arvif, ++ &fops_nss_dump_mpp_table); ++ ++ debugfs_create_file("mpath_add", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpath_add); ++ ++ debugfs_create_file("mpath_update", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpath_update); ++ ++ debugfs_create_file("mpath_del", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpath_del); ++ ++ debugfs_create_file("mpp_add", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpp_add); ++ ++ debugfs_create_file("mpp_update", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpp_update); ++ ++ debugfs_create_file("mpp_del", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_mpp_del); ++ ++ debugfs_create_file("assoc_link", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_assoc_link); ++ ++ debugfs_create_file("vap_linkid", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_vap_link_id); ++ ++ debugfs_create_file("excep_flags", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_excep_flags); ++ ++ debugfs_create_file("metadata_type", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_metadata_type); ++ ++ debugfs_create_file("exc_rate_limit", 0200, ++ debugfs_dbg_infra, arvif, ++ &fops_nss_exc_rate_limit); ++} ++ ++void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab) ++{ ++ struct dentry *debugfs_dbg_infra; ++ ++ debugfs_dbg_infra = debugfs_create_dir("dbg_infra", debugfs_ath11k); ++ ++ debugfs_create_file("links", 0200, ++ debugfs_dbg_infra, ab, ++ &fops_nss_links); ++ ++ debugfs_create_file("mpp_mode", 0600, ++ debugfs_dbg_infra, ab, ++ &fops_nss_mpp_mode); ++} ++ ++#endif +--- /dev/null ++++ b/drivers/net/wireless/ath/ath11k/debug_nss.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: BSD-3-Clause-Clear */ ++/* ++ * Copyright (c) 2020 The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef ATH11K_DEBUG_NSS_H ++#define ATH11K_DEBUG_NSS_H ++ ++#include ++#include ++ ++#define ATH11K_NSS_MPATH_DUMP_TIMEOUT (2 * HZ) ++ ++struct ath11k_vif; ++extern enum nss_wifi_mesh_mpp_learning_mode mpp_mode; ++extern struct list_head mesh_vaps; ++struct ath11k_nss_dbg_priv_data { ++ struct ath11k_vif *arvif; ++ char *buf; ++ unsigned int len; ++}; ++ ++#ifdef CPTCFG_MAC80211_DEBUGFS ++void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif); ++void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab); ++#else ++static inline void ath11k_debugfs_nss_mesh_vap_create(struct ath11k_vif *arvif) ++{ ++} ++static inline void ath11k_debugfs_nss_soc_create(struct ath11k_base *ab) ++{ ++} ++#endif ++ ++#endif +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -1453,15 +1453,29 @@ struct htt_ppdu_stats_usr_cmn_array { + struct htt_tx_ppdu_stats_info tx_ppdu_info[]; + } __packed; + ++#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_FLOW_TYPE GENMASK(7, 0) ++#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MPDU GENMASK(16, 8) ++#define HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MSDU GENMASK(30, 17) ++ ++struct htt_ppdu_stats_cmpltn_flush { ++ u32 drop_reason; ++ u32 info; ++ u8 tid_num; ++ u8 queue_type; ++ u16 sw_peer_id; ++} __packed; ++ + struct htt_ppdu_user_stats { + u16 peer_id; + u16 delay_ba; + u32 tlv_flags; + bool is_valid_peer_id; ++ bool rate_stats_updated; + struct htt_ppdu_stats_user_rate rate; + struct htt_ppdu_stats_usr_cmpltn_cmn cmpltn_cmn; + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status ack_ba; + struct htt_ppdu_stats_user_common common; ++ struct htt_ppdu_stats_cmpltn_flush cmpltn_flush; + }; + + #define HTT_PPDU_STATS_MAX_USERS 37 +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1409,6 +1409,71 @@ static int ath11k_htt_tlv_ppdu_stats_par + return 0; + } + ++static void ath11k_dp_ppdu_stats_flush_tlv_parse(struct ath11k_base *ab, ++ struct htt_ppdu_stats_cmpltn_flush *msg) ++{ ++ struct ath11k *ar; ++ struct ieee80211_sta *sta; ++ struct ath11k_sta *arsta; ++ struct ath11k_peer *peer = NULL; ++ struct ieee80211_tx_status status; ++ struct ieee80211_rate_status status_rate = { 0 }; ++ ++ if (!ab->nss.mesh_nss_offload_enabled) ++ return; ++ ++ rcu_read_lock(); ++ ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, msg->sw_peer_id); ++ if (!peer) ++ goto exit; ++ ++ if (peer->vif->type != NL80211_IFTYPE_MESH_POINT) ++ goto exit; ++ ++ if (ether_addr_equal(peer->addr, peer->vif->addr)) ++ goto exit; ++ ++ sta = peer->sta; ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ ++ memset(&status, 0, sizeof(status)); ++ ++ status.sta = sta; ++ status_rate.rate_idx = arsta->last_txrate; ++ ++ status.rates = &status_rate; ++ status.mpdu_fail = FIELD_GET(HTT_PPDU_STATS_CMPLTN_FLUSH_INFO_NUM_MPDU, ++ msg->info); ++ ar = arsta->arvif->ar; ++ ieee80211s_update_metric_ppdu(ar->hw, &status); ++ ++exit: ++ spin_unlock_bh(&ab->base_lock); ++ rcu_read_unlock(); ++} ++ ++static int ath11k_htt_tlv_ppdu_soc_stats_parse(struct ath11k_base *ab, ++ u16 tag, u16 len, const void *ptr, ++ void *data) ++{ ++ switch (tag) { ++ case HTT_PPDU_STATS_TAG_USR_COMPLTN_FLUSH: ++ if (len < sizeof(struct htt_ppdu_stats_cmpltn_flush)) { ++ ath11k_warn(ab, "Invalid len %d for the tag 0x%x\n", ++ len, tag); ++ return -EINVAL; ++ } ++ ath11k_dp_ppdu_stats_flush_tlv_parse(ab, (struct htt_ppdu_stats_cmpltn_flush *)ptr); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ + static void + ath11k_update_per_peer_tx_stats(struct ath11k *ar, + struct htt_ppdu_stats *ppdu_stats, u8 user) +@@ -1432,6 +1497,9 @@ ath11k_update_per_peer_tx_stats(struct a + if (!(usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_RATE))) + return; + ++ if (usr_stats->rate_stats_updated) ++ return; ++ + if (usr_stats->tlv_flags & BIT(HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON)) + is_ampdu = + HTT_USR_CMPLTN_IS_AMPDU(usr_stats->cmpltn_cmn.flags); +@@ -1565,6 +1633,8 @@ ath11k_update_per_peer_tx_stats(struct a + ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); + } + ++ usr_stats->rate_stats_updated = true; ++ + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); + } +@@ -1685,6 +1755,69 @@ int ath11k_dp_htt_tlv_iter(struct ath11k + return 0; + } + ++static void ++ath11k_dp_rx_ppdu_stats_update_tx_comp_status(struct ath11k *ar, ++ struct htt_ppdu_stats_info *ppdu_info) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ieee80211_sta *sta; ++ struct ath11k_sta *arsta; ++ struct ath11k_peer *peer = NULL; ++ struct htt_ppdu_user_stats* usr_stats = NULL; ++ struct ieee80211_tx_status status; ++ struct ieee80211_rate_status status_rate = { 0 }; ++ ++ u32 peer_id = 0; ++ int i; ++ ++ lockdep_assert_held(&ar->data_lock); ++ ++ if (!ar->ab->nss.mesh_nss_offload_enabled) ++ return; ++ ++ ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats); ++ ++ rcu_read_lock(); ++ ++ for (i = 0; i < ppdu_info->ppdu_stats.common.num_users; i++) { ++ usr_stats = &ppdu_info->ppdu_stats.user_stats[i]; ++ peer_id = usr_stats->peer_id; ++ spin_lock_bh(&ab->base_lock); ++ peer = ath11k_peer_find_by_id(ab, peer_id); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ continue; ++ } ++ ++ if (peer->vif->type != NL80211_IFTYPE_MESH_POINT) { ++ spin_unlock_bh(&ab->base_lock); ++ goto exit; ++ } ++ ++ if (ether_addr_equal(peer->addr, peer->vif->addr)) { ++ spin_unlock_bh(&ab->base_lock); ++ continue; ++ } ++ ++ sta = peer->sta; ++ arsta = (struct ath11k_sta *)sta->drv_priv; ++ ++ memset(&status, 0, sizeof(status)); ++ ++ status.sta = sta; ++ status_rate.rate_idx = arsta->last_txrate; ++ status.rates = &status_rate; ++ status.mpdu_succ = usr_stats->cmpltn_cmn.mpdu_success; ++ ++ ieee80211s_update_metric_ppdu(ar->hw, &status); ++ ++ spin_unlock_bh(&ab->base_lock); ++ } ++ ++exit: ++ rcu_read_unlock(); ++} ++ + static int ath11k_htt_pull_ppdu_stats(struct ath11k_base *ab, + struct sk_buff *skb) + { +@@ -1703,6 +1836,15 @@ static int ath11k_htt_pull_ppdu_stats(st + pdev_id = FIELD_GET(HTT_T2H_PPDU_STATS_INFO_PDEV_ID, msg->info); + ppdu_id = msg->ppdu_id; + ++ if (pdev_id == 0) { ++ ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, ++ ath11k_htt_tlv_ppdu_soc_stats_parse, ++ NULL); ++ if (ret) ++ ath11k_warn(ab, "failed to parse tlv %d\n", ret); ++ return ret; ++ } ++ + rcu_read_lock(); + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { +@@ -1770,6 +1912,12 @@ static int ath11k_htt_pull_ppdu_stats(st + } + } + ++ /* Stats update for mesh interface used when nss-offload in mesh is enabled */ ++ if ((ppdu_info->frame_type == HTT_STATS_PPDU_FTYPE_DATA && ++ (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_RATE)) && ++ ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_TAG_USR_COMPLTN_COMMON))) ++ ath11k_dp_rx_ppdu_stats_update_tx_comp_status(ar, ppdu_info); ++ + out_unlock_data: + spin_unlock_bh(&ar->data_lock); + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -3469,6 +3469,18 @@ static void ath11k_mac_op_nss_bss_info_c + ath11k_warn(ar->ab, "failed to set ap_isolate in nss %d\n", ret); + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ if (changed & (BSS_CHANGED_NSS_MESH_TTL | ++ BSS_CHANGED_NSS_MESH_REFRESH_TIME | ++ BSS_CHANGED_NSS_MESH_FWD_ENABLED)) { ++ ret = ath11k_nss_mesh_config_update(vif, changed); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "failed to update mesh nss offload configuration %d\n", ++ ret); ++ } ++#endif ++ + mutex_unlock(&ar->conf_mutex); + } + +@@ -9709,6 +9721,28 @@ err_fallback: + return 0; + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static void ++ath11k_mac_op_config_mesh_offload_path(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ int ret; ++ ++ if (arvif->ar->ab->nss.debug_mode) { ++ ret = 0; ++ return; ++ } ++ ++ ret = ath11k_nss_mesh_config_path(ar, arvif, cmd, path); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to configure path entry to mesh table %d\n", ret); ++} ++#endif ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, +@@ -9766,6 +9800,9 @@ static const struct ieee80211_ops ath11k + .set_sar_specs = ath11k_mac_op_set_bios_sar_specs, + .remain_on_channel = ath11k_mac_op_remain_on_channel, + .cancel_remain_on_channel = ath11k_mac_op_cancel_remain_on_channel, ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ .config_mesh_offload_path = ath11k_mac_op_config_mesh_offload_path, ++#endif + }; + + static void ath11k_mac_update_ch_list(struct ath11k *ar, +@@ -10227,6 +10264,8 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, SUPPORTS_NSS_OFFLOAD); + wiphy_ext_feature_set(ar->hw->wiphy, + NL80211_EXT_FEATURE_VLAN_OFFLOAD); ++ if (ab->nss.mesh_nss_offload_enabled) ++ ieee80211_hw_set(ar->hw, SUPPORTS_MESH_NSS_OFFLOAD); + } + + ret = ieee80211_register_hw(ar->hw); +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -6,6 +6,7 @@ + #include "debug.h" + #include "mac.h" + #include "nss.h" ++#include "debug_nss.h" + #include "core.h" + #include "peer.h" + #include "dp_rx.h" +@@ -14,6 +15,11 @@ + #include "wmi.h" + #include "../../../../../net/mac80211/sta_info.h" + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++enum nss_wifi_mesh_mpp_learning_mode mpp_mode = NSS_WIFI_MESH_MPP_LEARNING_MODE_INDEPENDENT_NSS; ++LIST_HEAD(mesh_vaps); ++#endif ++ + /*-----------------------------ATH11K-NSS Helpers--------------------------*/ + + static enum ath11k_nss_opmode +@@ -32,6 +38,30 @@ ath11k_nss_get_vdev_opmode(struct ath11k + return ATH11K_NSS_OPMODE_UNKNOWN; + } + ++static struct ath11k_vif *ath11k_nss_get_arvif_from_dev(struct net_device *dev) ++{ ++ struct wireless_dev *wdev; ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ ++ if (!dev) ++ return NULL; ++ ++ wdev = dev->ieee80211_ptr; ++ if (!wdev) ++ return NULL; ++ ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) ++ return NULL; ++ ++ arvif = (struct ath11k_vif *)vif->drv_priv; ++ if (!arvif) ++ return NULL; ++ ++ return arvif; ++} ++ + static void ath11k_nss_wifili_stats_sync(struct ath11k_base *ab, + struct nss_wifili_stats_sync_msg *wlsoc_stats) + { +@@ -263,7 +293,6 @@ void ath11k_nss_wifili_event_receive(str + ab->nss.response = response; + complete(&ab->nss.complete); + break; +- + case NSS_WIFILI_PEER_CREATE_MSG: + if (response != NSS_CMN_RESPONSE_EMSG) + break; +@@ -333,6 +362,13 @@ void ath11k_nss_wifili_event_receive(str + ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "nss wifili peer 4addr event received %d response %d error %d\n", + msg_type, response, error); + break; ++ case NSS_WIFILI_SEND_MESH_CAPABILITY_INFO: ++ complete(&ab->nss.complete); ++ if (response != NSS_CMN_RESPONSE_EMSG) ++ ab->nss.mesh_nss_offload_enabled = true; ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss wifili mesh capability response %d\n", ++ ab->nss.mesh_nss_offload_enabled); ++ break; + default: + ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type); + break; +@@ -420,7 +456,9 @@ ath11k_nss_wifili_ext_callback_fn(struct + ath11k_nss_process_mic_error(ab, skb); + break; + default: +- kfree(skb); ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "unknown packet type received in wifili ext cb %d", ++ wepm->pkt_type); ++ dev_kfree_skb_any(skb); + break; + } + } +@@ -735,8 +773,6 @@ ath11k_nss_vdev_special_data_receive(str + { + struct nss_wifi_vdev_per_packet_metadata *wifi_metadata = NULL; + struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata = NULL; +- struct wireless_dev *wdev; +- struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_base *ab; + bool drop = false; +@@ -744,24 +780,7 @@ ath11k_nss_vdev_special_data_receive(str + int data_offs = 0; + int ret = 0; + +- if (!dev) { +- dev_kfree_skb_any(skb); +- return; +- } +- +- wdev = dev->ieee80211_ptr; +- if (!wdev) { +- dev_kfree_skb_any(skb); +- return; +- } +- +- vif = wdev_to_ieee80211_vif(wdev); +- if (!vif) { +- dev_kfree_skb_any(skb); +- return; +- } +- +- arvif = (struct ath11k_vif *)vif->drv_priv; ++ arvif = ath11k_nss_get_arvif_from_dev(dev); + if (!arvif) { + dev_kfree_skb_any(skb); + return; +@@ -874,25 +893,1041 @@ static void + ath11k_nss_ext_vdev_data_receive(struct net_device *dev, struct sk_buff *skb, + __attribute__((unused)) struct napi_struct *napi) + { +- struct wireless_dev *wdev; +- struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_base *ab; + bool eth_decap = false; + int data_offs = 0; + int ret; + +- if (!dev) { ++ arvif = ath11k_nss_get_arvif_from_dev(dev); ++ if (!arvif) { + dev_kfree_skb_any(skb); + return; + } + +- wdev = dev->ieee80211_ptr; +- if (!wdev) { ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ /* log the original skb received from nss */ ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss ext : ", ++ skb->data, skb->len); ++ ++ ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap); ++ if (ret) { ++ ath11k_warn(ab, "error in nss ext rx undecap, type %d err %d\n", ++ arvif->nss.decap, ret); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++} ++ ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++/*------Mesh offload------*/ ++ ++void ath11k_nss_mesh_wifili_event_receive(void *app_data, ++ struct nss_cmn_msg *cmn_msg) ++{ ++ struct nss_wifi_mesh_msg *msg = (struct nss_wifi_mesh_msg *)cmn_msg; ++ struct ath11k_base *ab = app_data; ++ u32 msg_type = msg->cm.type; ++ enum nss_cmn_response response = msg->cm.response; ++ u32 error = msg->cm.error; ++ ++ if (!ab) ++ return; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss mesh event received %d response %d error %d\n", ++ msg_type, response, error); ++ ++ switch (msg_type) { ++ case NSS_WIFI_MESH_MSG_MPATH_ADD: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to add an entry to mpath table mesh_da %pM vdev_id %d\n", ++ (&msg->msg.mpath_add)->dest_mac_addr, ++ (&msg->msg.mpath_add)->link_vap_id); ++ break; ++ case NSS_WIFI_MESH_MSG_MPATH_UPDATE: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab, "failed to update mpath entry mesh_da %pM vdev_id %d" ++ "next_hop %pM old_next_hop %pM metric %d flags 0x%u hop_count %d" ++ "exp_time %u mesh_gate %u\n", ++ (&msg->msg.mpath_update)->dest_mac_addr, ++ (&msg->msg.mpath_update)->link_vap_id, ++ (&msg->msg.mpath_update)->next_hop_mac_addr, ++ (&msg->msg.mpath_update)->old_next_hop_mac_addr, ++ (&msg->msg.mpath_update)->metric, ++ (&msg->msg.mpath_update)->path_flags, ++ (&msg->msg.mpath_update)->hop_count, ++ (&msg->msg.mpath_update)->expiry_time, ++ (&msg->msg.mpath_update)->is_mesh_gate); ++ break; ++ case NSS_WIFI_MESH_MSG_MPATH_DELETE: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to remove mpath entry mesh_da %pM" ++ "vdev_id %d\n", ++ (&msg->msg.mpath_del)->mesh_dest_mac_addr, ++ (&msg->msg.mpath_del)->link_vap_id); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_ADD: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to add proxy entry da %pM mesh_da %pM \n", ++ (&msg->msg.proxy_add_msg)->dest_mac_addr, ++ (&msg->msg.proxy_add_msg)->mesh_dest_mac); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_UPDATE: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to update proxy path da %pM mesh_da %pM\n", ++ (&msg->msg.proxy_update_msg)->dest_mac_addr, ++ (&msg->msg.proxy_update_msg)->mesh_dest_mac); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_DELETE: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to remove proxy path entry da %pM mesh_da %pM\n", ++ (&msg->msg.proxy_del_msg)->dest_mac_addr, ++ (&msg->msg.proxy_del_msg)->mesh_dest_mac_addr); ++ break; ++ case NSS_WIFI_MESH_MSG_EXCEPTION_FLAG: ++ if (response == NSS_CMN_RESPONSE_EMSG) ++ ath11k_warn(ab,"failed to add the exception da %pM\n", ++ (&msg->msg.exception_msg)->dest_mac_addr); ++ break; ++ default: ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "unhandled event %d\n", msg_type); ++ break; ++ } ++} ++ ++static void nss_mesh_convert_path_flags(u8 *dest, u8 *src, bool to_nss) ++{ ++ if (to_nss) { ++ if (*src & IEEE80211_MESH_PATH_ACTIVE) ++ *dest |= NSS_WIFI_MESH_PATH_FLAG_ACTIVE; ++ if (*src & IEEE80211_MESH_PATH_RESOLVING) ++ *dest |= NSS_WIFI_MESH_PATH_FLAG_RESOLVING; ++ if (*src & IEEE80211_MESH_PATH_RESOLVED) ++ *dest |= NSS_WIFI_MESH_PATH_FLAG_RESOLVED; ++ if (*src & IEEE80211_MESH_PATH_FIXED) ++ *dest |= NSS_WIFI_MESH_PATH_FLAG_FIXED; ++ } else { ++ if (*src & NSS_WIFI_MESH_PATH_FLAG_ACTIVE) ++ *dest |= IEEE80211_MESH_PATH_ACTIVE; ++ if (*src & NSS_WIFI_MESH_PATH_FLAG_RESOLVING) ++ *dest |= IEEE80211_MESH_PATH_RESOLVING; ++ if (*src & NSS_WIFI_MESH_PATH_FLAG_RESOLVED) ++ *dest |= IEEE80211_MESH_PATH_RESOLVED; ++ if (*src & NSS_WIFI_MESH_PATH_FLAG_FIXED) ++ *dest |= IEEE80211_MESH_PATH_FIXED; ++ } ++} ++ ++static void ath11k_nss_mesh_mpath_refresh(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_path_refresh_msg *refresh_msg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ refresh_msg = &msg->msg.path_refresh_msg; ++ ether_addr_copy(path.mesh_da, refresh_msg->dest_mac_addr); ++ ether_addr_copy(path.next_hop, refresh_msg->next_hop_mac_addr); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh path refresh event from nss, mDA %pM next_hop %pM link_vdev %d\n", ++ refresh_msg->dest_mac_addr, refresh_msg->next_hop_mac_addr, ++ refresh_msg->link_vap_id); ++ ++ ++ if (ab->nss.debug_mode) ++ return; ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH); ++ if (ret) ++ ath11k_warn(ab, "failed to notify mpath refresh nss event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_path_not_found(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_mpath_not_found_msg *err_msg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ err_msg = &msg->msg.mpath_not_found_msg; ++ ether_addr_copy(path.da, err_msg->dest_mac_addr); ++ if (err_msg->is_mesh_forward_path) ++ ether_addr_copy(path.ta, err_msg->transmitter_mac_addr); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh path not found event from nss, (m)DA %pM ta %pM link vap %d\n", ++ err_msg->dest_mac_addr, err_msg->transmitter_mac_addr, err_msg->link_vap_id); ++ ++ ++ if (ab->nss.debug_mode) ++ return; ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND); ++ if (ret) ++ ath11k_warn(ab, "failed to notify mpath not found nss event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_path_delete(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_mpath_del_msg *del_msg = &msg->msg.mpath_del; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ ether_addr_copy(path.mesh_da, del_msg->mesh_dest_mac_addr); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh path delete event from nss, mDA %pM vap_id %d\n", ++ del_msg->mesh_dest_mac_addr, del_msg->link_vap_id); ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL); ++ if (ret) ++ ath11k_warn(ab, "failed to notify mpath delete nss event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_path_expiry(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_path_expiry_msg *exp_msg = &msg->msg.path_expiry_msg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ ether_addr_copy(path.mesh_da, exp_msg->mesh_dest_mac_addr); ++ ether_addr_copy(path.next_hop, exp_msg->next_hop_mac_addr); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh path delete event from nss, mDA %pM next_hop %pM if_num %d\n", ++ exp_msg->mesh_dest_mac_addr, exp_msg->next_hop_mac_addr, ++ arvif->nss.if_num); ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP); ++ if (ret) ++ ath11k_warn(ab, "failed to notify mpath expiry nss event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_mpp_learn(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++ { ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_proxy_path_learn_msg *learn_msg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ learn_msg = &msg->msg.proxy_learn_msg; ++ ++ ether_addr_copy(path.mesh_da, learn_msg->mesh_dest_mac); ++ ether_addr_copy(path.da, learn_msg->dest_mac_addr); ++ nss_mesh_convert_path_flags(&path.flags, &learn_msg->path_flags, false); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh proxy learn event from nss, mDA %pM da %pM flags 0x%x if_num %d\n", ++ learn_msg->mesh_dest_mac, learn_msg->dest_mac_addr, ++ learn_msg->path_flags, arvif->nss.if_num); ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN); ++ if (ret) ++ ath11k_warn(ab, "failed to notify proxy learn event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_mpp_add(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_proxy_path_add_msg *add_msg = &msg->msg.proxy_add_msg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ ether_addr_copy(path.mesh_da, add_msg->mesh_dest_mac); ++ ether_addr_copy(path.da, add_msg->dest_mac_addr); ++ nss_mesh_convert_path_flags(&path.flags, &add_msg->path_flags, false); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh proxy add event from nss, mDA %pM da %pM flags 0x%x if_num %d\n", ++ add_msg->mesh_dest_mac, add_msg->dest_mac_addr, add_msg->path_flags, ++ arvif->nss.if_num); ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD); ++ if (ret) ++ ath11k_warn(ab, "failed to notify proxy add event %d\n", ret); ++} ++ ++static void ath11k_nss_mesh_mpp_update(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_proxy_path_update_msg *umsg; ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ umsg = &msg->msg.proxy_update_msg; ++ ether_addr_copy(path.mesh_da, umsg->mesh_dest_mac); ++ ether_addr_copy(path.da, umsg->dest_mac_addr); ++ nss_mesh_convert_path_flags(&path.flags, &umsg->path_flags, false); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "Mesh proxy update event from nss, mDA %pM da %pM flags 0x%x if_num %d\n", ++ umsg->mesh_dest_mac, umsg->dest_mac_addr, umsg->path_flags, arvif->nss.if_num); ++ ++ ret = ieee80211_mesh_path_offld_change_notify(arvif->vif, &path, ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE); ++ if (ret) ++ ath11k_warn(ab, "failed to notify proxy update event %d\n", ret); ++} ++ ++static int ++ath11k_nss_mesh_process_path_table_dump_msg(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct nss_wifi_mesh_path_table_dump *mpath_dump = &msg->msg.mpath_table_dump; ++ struct ath11k_nss_mpath_entry *entry; ++ struct ath11k *ar = arvif->ar; ++ ssize_t len; ++ ++ len = sizeof(struct nss_wifi_mesh_path_dump_entry) * mpath_dump->num_entries; ++ entry = kzalloc(sizeof(*entry) + len, GFP_ATOMIC); ++ if (!entry) ++ return -ENOMEM; ++ ++ memcpy(entry->mpath, mpath_dump->path_entry, len); ++ entry->num_entries = mpath_dump->num_entries; ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_add_tail(&entry->list, &arvif->nss.mpath_dump); ++ arvif->nss.mpath_dump_num_entries += mpath_dump->num_entries; ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ if (!mpath_dump->more_events) ++ complete(&arvif->nss.dump_mpath_complete); ++ ++ return 0; ++} ++ ++static int ++ath11k_nss_mesh_process_mpp_table_dump_msg(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_msg *msg) ++{ ++ struct nss_wifi_mesh_proxy_path_table_dump *mpp_dump; ++ struct ath11k_nss_mpp_entry *entry, *tmp; ++ struct ath11k *ar = arvif->ar; ++ struct arvif_nss *nss = &arvif->nss; ++ ssize_t len; ++ LIST_HEAD(local_entry_exp_update); ++ ++ mpp_dump = &msg->msg.proxy_path_table_dump; ++ ++ if (!mpp_dump->num_entries) ++ return 0; ++ ++ len = sizeof(struct nss_wifi_mesh_proxy_path_dump_entry) * mpp_dump->num_entries; ++ entry = kzalloc(sizeof(*entry) + len, GFP_ATOMIC); ++ if (!entry) ++ return -ENOMEM; ++ ++ memcpy(entry->mpp, mpp_dump->path_entry, len); ++ entry->num_entries = mpp_dump->num_entries; ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_add_tail(&entry->list, &arvif->nss.mpp_dump); ++ arvif->nss.mpp_dump_num_entries += mpp_dump->num_entries; ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ if (!mpp_dump->more_events) { ++ if (arvif->nss.mpp_aging) { ++ arvif->nss.mpp_aging = false; ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpp_dump, &local_entry_exp_update); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(entry, tmp, &local_entry_exp_update, list) { ++ if (entry->mpp->time_diff > ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS) ++ continue; ++ mesh_nss_offld_proxy_path_exp_update(arvif->vif, ++ entry->mpp->dest_mac_addr, ++ entry->mpp->mesh_dest_mac, ++ entry->mpp->time_diff); ++ } ++ /* If mpp_dump_req is true dont free the entry ++ * since it will get freed in debug_nss_fill_mpp_dump ++ * both mpp_aging and mpp_dump_req will be true during ++ * simultaneous accessing of mpp dump entry. So this will ++ * gain the reuse of same dump result for both mpp_aging ++ * and mpp_dump_req */ ++ if (!arvif->nss.mpp_dump_req) { ++ list_for_each_entry_safe(entry, tmp, &local_entry_exp_update, list) ++ kfree(entry); ++ } else { ++ /* Adding back to global nss dump tbl to reuse the same ++ * tbl for mpp dump request ++ */ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&local_entry_exp_update, &nss->mpp_dump); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ } ++ } ++ ++ if (arvif->nss.mpp_dump_req) { ++ complete(&arvif->nss.dump_mpp_complete); ++ arvif->nss.mpp_dump_req = false; ++ } ++ } ++ ++ return 0; ++} ++ ++int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_exception_flag_msg *nss_msg) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_path_exception(arvif->nss.mesh_handle, nss_msg, ++ msg_cb, arvif->ar->ab); ++ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(arvif->ar->ab, "failed to set the exception flags\n"); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int ath11k_nss_exc_rate_config(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_rate_limit_config *nss_exc_cfg) ++{ ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_config_mesh_exception_sync(arvif->nss.mesh_handle, nss_exc_cfg); ++ ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(arvif->ar->ab, "failed to set the exception rate ctrl\n"); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void ath11k_nss_mesh_obj_vdev_event_receive(void *dev, ++ struct nss_cmn_msg *cmn_msg) ++{ ++ struct nss_wifi_mesh_msg *msg = (struct nss_wifi_mesh_msg *) cmn_msg; ++ struct ath11k_base *ab; ++ struct ath11k_vif *arvif; ++ int ret; ++ ++ arvif = ath11k_nss_get_arvif_from_dev(dev); ++ if (!arvif) ++ return; ++ ++ ab = arvif->ar->ab; ++ ++ switch (msg->cm.type) { ++ case NSS_WIFI_MESH_MSG_PATH_REFRESH: ++ ath11k_nss_mesh_mpath_refresh(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PATH_NOT_FOUND: ++ ath11k_nss_mesh_path_not_found(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_MPATH_DELETE: ++ ath11k_nss_mesh_path_delete(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PATH_EXPIRY: ++ ath11k_nss_mesh_path_expiry(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_LEARN: ++ ath11k_nss_mesh_mpp_learn(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_ADD: ++ ath11k_nss_mesh_mpp_add(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_UPDATE: ++ ath11k_nss_mesh_mpp_update(arvif, msg); ++ break; ++ case NSS_WIFI_MESH_MSG_PATH_TABLE_DUMP: ++ ret = ath11k_nss_mesh_process_path_table_dump_msg(arvif, msg); ++ if (ret) ++ ath11k_warn(arvif->ar->ab, "failed mpath table dump message %d\n", ++ ret); ++ break; ++ case NSS_WIFI_MESH_MSG_PROXY_PATH_TABLE_DUMP: ++ ret = ath11k_nss_mesh_process_mpp_table_dump_msg(arvif, msg); ++ if (ret) ++ ath11k_warn(arvif->ar->ab, "failed mpp table dump message %d\n", ++ ret); ++ break; ++ default: ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "unknown message type on mesh obj vap %d\n", ++ msg->cm.type); ++ break; ++ } ++} ++ ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static int ath11k_nss_mesh_mpath_add(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_mpath_add_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "add mpath for mesh_da %pM on radio %d\n", ++ path->mesh_da, ar->pdev->pdev_id); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_add_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->dest_mac_addr, path->mesh_da); ++ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop); ++ msg->hop_count = path->hop_count; ++ msg->metric = path->metric; ++ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true); ++ msg->link_vap_id = arvif->nss.if_num; ++ msg->block_mesh_fwd = path->block_mesh_fwd; ++ msg->metadata_type = path->metadata_type ? NSS_WIFI_MESH_PRE_HEADER_80211: NSS_WIFI_MESH_PRE_HEADER_NONE; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_path_add(arvif->nss.mesh_handle, msg, ++ msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to add mpath entry mesh_da %pM radio_id %d status %d\n", ++ path->mesh_da, arvif->nss.if_num, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_mpath_update(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_mpath_update_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, ++ "update mpath mesh_da %pM radio %d next_hop %pM old_next_hop %pM " ++ "metric %d flags 0x%x hop_count %d " ++ "exp_time %lu mesh_gate %d\n", ++ path->mesh_da, ar->pdev->pdev_id, path->next_hop, path->old_next_hop, ++ path->metric, path->flags, path->hop_count, path->exp_time, ++ path->mesh_gate); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_update_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->dest_mac_addr, path->mesh_da); ++ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop); ++ ether_addr_copy(msg->old_next_hop_mac_addr, path->old_next_hop); ++ msg->hop_count = path->hop_count; ++ msg->metric = path->metric; ++ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true); ++ msg->link_vap_id = arvif->nss.if_num; ++ msg->is_mesh_gate = path->mesh_gate; ++ msg->expiry_time = path->exp_time; ++ msg->block_mesh_fwd = path->block_mesh_fwd; ++ msg->metadata_type = ++ (uint8_t)(path->metadata_type == ++ (uint8_t) ++ NSS_WIFI_MESH_PRE_HEADER_80211 ? ++ NSS_WIFI_MESH_PRE_HEADER_80211 : ++ NSS_WIFI_MESH_PRE_HEADER_NONE); ++ ++ msg->update_flags = NSS_WIFI_MESH_PATH_UPDATE_FLAG_NEXTHOP | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_HOPCOUNT | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_METRIC | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_MESH_FLAGS | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_BLOCK_MESH_FWD | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_METADATA_ENABLE_VALID; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_path_update(arvif->nss.mesh_handle, msg, ++ msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to update mpath entry mesh_da %pM radio_id %d status %d\n", ++ path->mesh_da, arvif->nss.if_num, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_mpath_del(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_mpath_del_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "del mpath for mesh_da %pM on radio %d\n", ++ path->mesh_da, ar->pdev->pdev_id); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_mpath_del_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->mesh_dest_mac_addr, path->mesh_da); ++ ether_addr_copy(msg->next_hop_mac_addr, path->next_hop); ++ msg->link_vap_id = arvif->nss.if_num; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_path_delete(arvif->nss.mesh_handle, ++ msg, msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to del mpath entry mesh_da %pM radio_id %d status %d\n", ++ path->mesh_da, arvif->nss.if_num, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_mpp_add_cmd(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_proxy_path_add_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "add mpp mesh_da %pM da %pM\n", ++ path->mesh_da, path->da); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_add_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->dest_mac_addr, path->da); ++ ether_addr_copy(msg->mesh_dest_mac, path->mesh_da); ++ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true); ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_proxy_path_add(arvif->nss.mesh_handle, ++ msg, msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to add mpp entry da %pM mesh_da %pM status %d\n", ++ path->da, path->mesh_da, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_mpp_update_cmd(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_proxy_path_update_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "update mpp da %pM mesh_da %pM on vap_id %d\n", ++ path->da, path->mesh_da, arvif->nss.if_num); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_update_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->dest_mac_addr, path->da); ++ ether_addr_copy(msg->mesh_dest_mac, path->mesh_da); ++ nss_mesh_convert_path_flags(&msg->path_flags, &path->flags, true); ++ msg->bitmap = NSS_WIFI_MESH_PATH_UPDATE_FLAG_NEXTHOP | ++ NSS_WIFI_MESH_PATH_UPDATE_FLAG_HOPCOUNT; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_proxy_path_update(arvif->nss.mesh_handle, ++ msg, msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to update mpp da %pM mesh_da %pM status %d\n", ++ path->da, path->mesh_da, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_mpp_del_cmd(struct ath11k_vif *arvif, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct nss_wifi_mesh_proxy_path_del_msg *msg; ++ struct ath11k *ar = arvif->ar; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS_MESH, "del mpath for mesh_da %pM\n", ++ path->mesh_da); ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_proxy_path_del_msg), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ ether_addr_copy(msg->dest_mac_addr, path->da); ++ ether_addr_copy(msg->mesh_dest_mac_addr, path->mesh_da); ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_proxy_path_delete(arvif->nss.mesh_handle, msg, ++ msg_cb, ar->ab); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, ++ "failed to add mpath entry mesh_da %pM status %d\n", ++ path->mesh_da, status); ++ ret = -EINVAL; ++ } ++ ++ kfree(msg); ++ ++ return ret; ++} ++ ++int ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ int ret; ++ ++ ++ if (!ar->ab->nss.enabled) ++ return 0; ++ ++ switch (cmd) { ++ case IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH: ++ ret = ath11k_nss_mesh_mpath_add(arvif, path); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH: ++ ret = ath11k_nss_mesh_mpath_update(arvif, path); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH: ++ ret = ath11k_nss_mesh_mpath_del(arvif, path); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP: ++ ret = ath11k_nss_mesh_mpp_add_cmd(arvif, path); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP: ++ ret = ath11k_nss_mesh_mpp_update_cmd(arvif, path); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP: ++ ret = ath11k_nss_mesh_mpp_del_cmd(arvif, path); ++ break; ++ default: ++ ath11k_warn(ar->ab, "unknown mesh path table command type %d\n", cmd); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed) ++{ ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_config_msg *nss_msg; ++ struct arvif_nss *nss = &arvif->nss; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ if (!ab->nss.enabled) ++ return 0; ++ ++ if (!ab->nss.mesh_nss_offload_enabled) ++ return -ENOTSUPP; ++ ++ if (!changed) ++ return 0; ++ ++ nss_msg = kzalloc(sizeof(*nss_msg), GFP_KERNEL); ++ if (!nss_msg) ++ return -ENOMEM; ++ ++ if (changed & BSS_CHANGED_NSS_MESH_TTL) { ++ nss_msg->ttl = vif->bss_conf.nss_offld_ttl; ++ nss->mesh_ttl = vif->bss_conf.nss_offld_ttl; ++ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_TTL_VALID; ++ } ++ ++ if (changed & BSS_CHANGED_NSS_MESH_REFRESH_TIME) { ++ nss_msg->mesh_path_refresh_time = ++ vif->bss_conf.nss_offld_mpath_refresh_time; ++ nss->mpath_refresh_time = ++ vif->bss_conf.nss_offld_mpath_refresh_time; ++ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_MPATH_REFRESH_VALID; ++ } ++ ++ if (changed & BSS_CHANGED_NSS_MESH_FWD_ENABLED) { ++ nss_msg->block_mesh_forwarding = ++ vif->bss_conf.nss_offld_mesh_forward_enabled; ++ nss->mesh_forward_enabled = ++ vif->bss_conf.nss_offld_mesh_forward_enabled; ++ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_BLOCK_MESH_FWD_VALID; ++ nss_msg->metadata_type = arvif->nss.metadata_type; ++ nss_msg->config_flags |= NSS_WIFI_MESH_CONFIG_FLAG_METADATA_ENABLE_VALID; ++ } ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_mesh_config_update_sync(arvif->nss.mesh_handle, ++ nss_msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failed to configure nss mesh obj vdev nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ } ++ ++ kfree(nss_msg); ++ ++ return ret; ++} ++#endif ++ ++int ath11k_nss_dump_mpath_request(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct ath11k *ar = arvif->ar; ++ struct arvif_nss *nss = &arvif->nss; ++ struct ath11k_nss_mpath_entry *entry, *tmp; ++ LIST_HEAD(local_entry); ++ nss_tx_status_t status; ++ ++ /* Clean up any stale entries from old events */ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail(&nss->mpath_dump, &local_entry); ++ arvif->nss.mpath_dump_num_entries = 0; ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(entry, tmp, &local_entry, list) ++ kfree(entry); ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_dump_mesh_path_sync(arvif->nss.mesh_handle); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failed to send mpath dump command on mesh obj vdev nss_err:%d\n", ++ status); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int ath11k_nss_dump_mpp_request(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct ath11k *ar = arvif->ar; ++ struct arvif_nss *nss = &arvif->nss; ++ struct ath11k_nss_mpp_entry *entry, *tmp; ++ LIST_HEAD(local_entry); ++ nss_wifi_meshmgr_status_t status; ++ ++ if (!arvif->nss.mpp_aging) { ++ /* Clean up any stale entries from old events */ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpp_dump, &local_entry); ++ arvif->nss.mpp_dump_num_entries = 0; ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(entry, tmp, &local_entry, list) { ++ list_del(&entry->list); ++ kfree(entry); ++ } ++ } ++ ++ arvif->nss.mpp_dump_req = true; ++ ++ status = nss_wifi_meshmgr_dump_mesh_proxy_path_sync(arvif->nss.mesh_handle); ++ if (status != NSS_WIFI_MESHMGR_SUCCESS) { ++ if (status == NSS_WIFI_MESHMGR_FAILURE_ONESHOT_ALREADY_ATTACHED) ++ return 0; ++ ath11k_warn(ab, "failed to send mpp dump command on mesh obj vdev nss_err:%d\n", ++ status); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++void ath11k_nss_mpp_timer_cb(struct timer_list *timer) ++{ ++ nss_wifi_mesh_msg_callback_t msg_cb; ++ struct arvif_nss *nss = from_timer(nss, timer,mpp_expiry_timer); ++ struct ath11k_vif *arvif = container_of(nss, struct ath11k_vif, nss); ++ struct ath11k_base *ab = arvif->ar->ab; ++ LIST_HEAD(local_entry); ++ nss_tx_status_t status; ++ ++ msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ ++ if (!arvif->nss.mpp_dump_req) ++ arvif->nss.mpp_dump_num_entries = 0; ++ arvif->nss.mpp_aging = true; ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_dump_mesh_proxy_path(arvif->nss.mesh_handle, msg_cb, ab); ++ if (status != NSS_TX_SUCCESS) ++ ath11k_warn(ab, "failed to send mpp dump command from timer nss_err:%d\n", ++ status); ++ ++ mod_timer(&nss->mpp_expiry_timer, ++ jiffies + msecs_to_jiffies(ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS)); ++ ++} ++ ++static void ++ath11k_nss_mesh_obj_vdev_data_receive(struct net_device *dev, struct sk_buff *skb, ++ struct napi_struct *napi) ++{ ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ char dump_msg[100] = {0}; ++ struct nss_wifi_mesh_per_packet_metadata *wifi_metadata = NULL; ++ ++ arvif = ath11k_nss_get_arvif_from_dev(dev); ++ if (!arvif) { + dev_kfree_skb_any(skb); + return; + } + ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ snprintf(dump_msg, sizeof(dump_msg), "nss mesh obj vdev: link id %d ", ++ arvif->nss.if_num); ++ ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "dp rx msdu from nss", dump_msg, ++ skb->data, skb->len); ++ ++ if (arvif->nss.metadata_type == NSS_WIFI_MESH_PRE_HEADER_80211) { ++ wifi_metadata = (struct nss_wifi_mesh_per_packet_metadata *)(skb->data - ++ (sizeof(struct nss_wifi_mesh_per_packet_metadata))); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, ++ "exception from nss on mesh obj vap: pkt_type %d\n", ++ wifi_metadata->pkt_type); ++ switch (wifi_metadata->pkt_type) { ++ case NSS_WIFI_MESH_PRE_HEADER_80211: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "wifi header from nss on mesh obj vdev: ", ++ skb->data - sizeof(*wifi_metadata), sizeof(*wifi_metadata) + skb->len); ++ dev_kfree_skb_any(skb); ++ break; ++ default: ++ dev_kfree_skb_any(skb); ++ } ++ ++ return; ++ } ++ ++ ath11k_nss_deliver_rx(arvif->vif, skb, true, 0, napi); ++} ++ ++static void ++ath11k_nss_mesh_obj_ext_data_callback(struct net_device *dev, struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ struct nss_wifi_mesh_encap_ext_pkt_metadata *wifi_metadata = NULL; ++ int metadata_len; ++ ++ arvif = ath11k_nss_get_arvif_from_dev(dev); ++ if (!arvif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ ab = arvif->ar->ab; ++ ++ skb->dev = dev; ++ ++ metadata_len = NSS_WIFI_MESH_ENCAP_METADATA_OFFSET_TYPE + ++ sizeof(struct nss_wifi_mesh_encap_ext_pkt_metadata); ++ ++ /* msdu from nss should contain metadata in headroom ++ * any msdu which has invalid or not contains metadata ++ * will be treated as invalid msdu and dropping it. ++ */ ++ if (!(metadata_len < skb_headroom(skb))) { ++ ath11k_warn(ab, "msdu from nss is having invalid headroom %d\n", skb_headroom(skb)); ++ dev_kfree_skb_any(skb); ++ return; ++ } ++ ++ dma_unmap_single(ab->dev, virt_to_phys(skb->head), ++ metadata_len, ++ DMA_FROM_DEVICE); ++ ++ wifi_metadata = (struct nss_wifi_mesh_encap_ext_pkt_metadata *)(skb->head + ++ NSS_WIFI_MESH_ENCAP_METADATA_OFFSET_TYPE); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "msdu from nss ext_data _cb on mesh obj vdev"); ++ ++ switch (wifi_metadata->pkt_type) { ++ case NSS_WIFI_MESH_ENCAP_EXT_DATA_PKT_TYPE_MPATH_NOT_FOUND_EXC: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "msdu from nss ext_data for mpath not found : ", ++ skb->data, skb->len); ++ skb->protocol = eth_type_trans(skb, dev); ++ skb_reset_network_header(skb); ++ dev_queue_xmit(skb); ++ break; ++ default: ++ ath11k_warn(ab, "unknown packet type received in mesh obj ext data %d", ++ wifi_metadata->pkt_type); ++ dev_kfree_skb_any(skb); ++ } ++} ++ ++static void ++ath11k_nss_mesh_link_vdev_data_receive(struct net_device *dev, ++ struct sk_buff *skb, ++ struct napi_struct *napi) ++{ ++ struct ieee80211_vif *vif; ++ struct ath11k_vif *arvif; ++ struct ath11k_base *ab; ++ struct wireless_dev *wdev = (struct wireless_dev *)dev; ++ + vif = wdev_to_ieee80211_vif(wdev); + if (!vif) { + dev_kfree_skb_any(skb); +@@ -906,23 +1941,81 @@ ath11k_nss_ext_vdev_data_receive(struct + } + + ab = arvif->ar->ab; ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "msdu from nss data_receive_cb on mesh link vdev: ", ++ skb->data, skb->len); ++ /* data callback for mesh link vap is not expected */ ++ dev_kfree_skb_any(skb); ++} + +- skb->dev = dev; ++static void ++ath11k_nss_mesh_link_vdev_special_data_receive(struct net_device *dev, ++ struct sk_buff *skb, ++ __attribute__((unused)) struct napi_struct *napi) ++{ ++ struct ieee80211_vif *vif; ++ struct ath11k_base *ab; ++ struct nss_wifi_vdev_per_packet_metadata *wifi_metadata = NULL; ++ struct ath11k_skb_rxcb *rxcb; ++ struct ath11k_vif *arvif; ++ struct wireless_dev *wdev = (struct wireless_dev *)dev; + +- /* log the original skb received from nss */ +- ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss ext : ", +- skb->data, skb->len); ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) { ++ dev_kfree_skb_any(skb); ++ return; ++ } + +- ret = ath11k_nss_undecap(arvif, skb, &data_offs, ð_decap); +- if (ret) { +- ath11k_warn(ab, "error in nss ext rx undecap, type %d err %d\n", +- arvif->nss.decap, ret); ++ arvif = (struct ath11k_vif *)vif->drv_priv; ++ if (!arvif) { + dev_kfree_skb_any(skb); + return; + } + +- ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++ ab = arvif->ar->ab; ++ ++ wifi_metadata = (struct nss_wifi_vdev_per_packet_metadata *)(skb->head + ++ NSS_WIFI_VDEV_PER_PACKET_METADATA_OFFSET); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, ++ "dp special data from nss on mesh link vap: pkt_type %d\n", ++ wifi_metadata->pkt_type); ++ ++ switch (wifi_metadata->pkt_type) { ++ case NSS_WIFI_VDEV_MESH_EXT_DATA_PKT_TYPE_RX_SPL_PACKET: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "special packet meta data from nss on mesh link vdev: ", ++ wifi_metadata, ++ sizeof(struct nss_wifi_vdev_per_packet_metadata)); ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "special packet payload from nss on mesh link vdev: ", ++ skb->data, skb->len); ++ dev_kfree_skb_any(skb); ++ break; ++ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_MCBC_RX: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "mcast packet exception from nss on mesh link vdev: ", ++ skb->data, skb->len); ++ rxcb = ATH11K_SKB_RXCB(skb); ++ rxcb->rx_desc = (struct hal_rx_desc *)skb->head; ++ rxcb->is_first_msdu = rxcb->is_last_msdu = true; ++ rxcb->is_continuation = false; ++ rxcb->is_mcbc = true; ++ ath11k_dp_rx_from_nss(arvif->ar, skb, napi); ++ break; ++ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_MESH: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "static exception path from nss on mesh link vdev: ", ++ skb->data, skb->len); ++ dev_kfree_skb_any(skb); ++ break; ++ default: ++ ath11k_warn(ab, "unknown packet type received in mesh link vdev %d", ++ wifi_metadata->pkt_type); ++ dev_kfree_skb_any(skb); ++ break; ++ } + } ++#endif + + int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb) + { +@@ -930,8 +2023,9 @@ int ath11k_nss_tx(struct ath11k_vif *arv + nss_tx_status_t status; + int encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); + struct ath11k_soc_dp_stats *soc_stats = &ar->ab->soc_stats; ++ char dump_msg[100] = {0}; + +- if (encap_type != arvif->nss.encap) { ++ if (!arvif->ar->ab->nss.debug_mode && encap_type != arvif->nss.encap) { + ath11k_warn(ar->ab, "encap mismatch in nss tx skb encap type %d" \ + " vif encap type %d\n", encap_type, arvif->nss.encap); + goto drop; +@@ -946,16 +2040,45 @@ int ath11k_nss_tx(struct ath11k_vif *arv + ath11k_nss_tx_encap_nwifi(skb); + + send: +- ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, +- arvif->vif->type == NL80211_IFTYPE_AP_VLAN ? "ext vdev" : "", +- "nss tx msdu: ", skb->data, skb->len); +- +- if (arvif->vif->type == NL80211_IFTYPE_AP_VLAN) ++ if (arvif->vif->type == NL80211_IFTYPE_AP_VLAN) { ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "ext vdev", ++ "nss tx msdu: ", skb->data, skb->len); + status = nss_wifi_ext_vdev_tx_buf(arvif->nss.ctx, skb, + arvif->nss.if_num); +- else +- status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, +- arvif->nss.if_num); ++ } else { ++ if (arvif->ar->ab->nss.debug_mode) { ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ if (encap_type == HAL_TCL_ENCAP_TYPE_ETHERNET && ++ !is_multicast_ether_addr(skb->data)) { ++ snprintf(dump_msg, sizeof(dump_msg), ++ "nss tx ucast msdu: %d ", ++ arvif->nss.mesh_handle); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "mesh", ++ dump_msg, skb->data, skb->len); ++ status = (nss_tx_status_t)nss_wifi_meshmgr_tx_buf(arvif->nss.mesh_handle, ++ skb); ++ } else { ++#endif ++ snprintf(dump_msg, sizeof(dump_msg), ++ "nss tx mcast msdu: %d ", ++ arvif->nss.if_num); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "mesh", ++ dump_msg, skb->data, skb->len); ++ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, ++ arvif->nss.if_num); ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ } ++#endif ++ } else { ++ snprintf(dump_msg, sizeof(dump_msg), ++ "nss tx msdu: %d ", ++ arvif->nss.if_num); ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DP_TX, "", ++ dump_msg, skb->data, skb->len); ++ status = nss_wifi_vdev_tx_buf(arvif->ar->nss.ctx, skb, ++ arvif->nss.if_num); ++ } ++ } + + if (status != NSS_TX_SUCCESS) { + ath11k_dbg(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX), +@@ -1057,6 +2180,9 @@ static int ath11k_nss_vdev_configure(str + + vdev_cfg = &vdev_msg->msg.vdev_config; + ++ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT) ++ vdev_cfg->vap_ext_mode = WIFI_VDEV_EXT_MODE_MESH_LINK; ++ + vdev_cfg->radio_ifnum = ar->nss.if_num; + vdev_cfg->vdev_id = arvif->vdev_id; + +@@ -1095,6 +2221,39 @@ free: + return ret; + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static int ath11k_nss_mesh_obj_assoc_link_vap(struct ath11k_vif *arvif) ++{ ++ struct nss_wifi_mesh_assoc_link_vap *msg; ++ struct ath11k_base *ab = arvif->ar->ab; ++ nss_tx_status_t status; ++ int ret; ++ ++ msg = kzalloc(sizeof(struct nss_wifi_mesh_assoc_link_vap), GFP_ATOMIC); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->link_vap_id = arvif->nss.if_num; ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss mesh assoc link vap %d, mesh handle %d\n", ++ arvif->nss.if_num, arvif->nss.mesh_handle); ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_assoc_link_vap_sync(arvif->nss.mesh_handle, msg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failed mesh obj vdev tx msg for assoc link vap nss_err:%d\n", ++ status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = 0; ++free: ++ kfree(msg); ++ ++ return ret; ++} ++#endif ++ + static void ath11k_nss_vdev_unregister(struct ath11k_vif *arvif) + { + struct ath11k_base *ab = arvif->ar->ab; +@@ -1106,6 +2265,14 @@ static void ath11k_nss_vdev_unregister(s + ath11k_dbg(ab, ATH11K_DBG_NSS, "unregistered nss vdev %d \n", + arvif->nss.if_num); + break; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ case NL80211_IFTYPE_MESH_POINT: ++ nss_unregister_wifi_vdev_if(arvif->nss.if_num); ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "unregistered nss mesh vdevs mesh link %d\n", ++ arvif->nss.if_num); ++ break; ++#endif + default: + ath11k_warn(ab, "unsupported interface type %d for nss vdev unregister\n", + arvif->vif->type); +@@ -1113,6 +2280,78 @@ static void ath11k_nss_vdev_unregister(s + } + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static int ath11k_nss_mesh_alloc_register(struct ath11k_vif *arvif, ++ struct net_device *netdev) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct nss_wifi_mesh_config_msg *nss_msg; ++ struct arvif_nss *nss = &arvif->nss; ++ int ret = 0; ++ ++ nss->mesh_ttl = ATH11K_MESH_DEFAULT_ELEMENT_TTL; ++ nss->mpath_refresh_time = 1000; /* msecs */ ++ nss->mesh_forward_enabled = true; ++ ++ nss_msg = kzalloc(sizeof(*nss_msg), GFP_KERNEL); ++ if (!nss_msg) ++ return -ENOMEM; ++ ++ nss_msg->ttl = nss->mesh_ttl; ++ nss_msg->mesh_path_refresh_time = nss->mpath_refresh_time; ++ nss_msg->mpp_learning_mode = mpp_mode; ++ nss_msg->block_mesh_forwarding = 0; ++ ether_addr_copy(nss_msg->local_mac_addr, arvif->vif->addr); ++ nss_msg->config_flags = ++ NSS_WIFI_MESH_CONFIG_FLAG_TTL_VALID | ++ NSS_WIFI_MESH_CONFIG_FLAG_MPATH_REFRESH_VALID | ++ NSS_WIFI_MESH_CONFIG_FLAG_MPP_LEARNING_MODE_VALID | ++ NSS_WIFI_MESH_CONFIG_FLAG_BLOCK_MESH_FWD_VALID | ++ NSS_WIFI_MESH_CONFIG_FLAG_LOCAL_MAC_VALID; ++ ++ arvif->nss.mesh_handle = nss_wifi_meshmgr_if_create_sync(netdev, nss_msg, ++ ath11k_nss_mesh_obj_vdev_data_receive, ++ ath11k_nss_mesh_obj_ext_data_callback, ++ ath11k_nss_mesh_obj_vdev_event_receive); ++ if (arvif->nss.mesh_handle == NSS_WIFI_MESH_HANDLE_INVALID) { ++ ath11k_warn(ab, "failed to create meshmgr\n"); ++ ret = -EINVAL; ++ } ++ ++ kfree(nss_msg); ++ ++ return ret; ++} ++ ++static int ath11k_nss_mesh_vdev_register(struct ath11k_vif *arvif, ++ struct net_device *netdev) ++{ ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_base *ab = ar->ab; ++ nss_tx_status_t status; ++ u32 features = 0; ++ ++ status = nss_register_wifi_vdev_if(ar->nss.ctx, ++ arvif->nss.if_num, ++ ath11k_nss_mesh_link_vdev_data_receive, ++ ath11k_nss_mesh_link_vdev_special_data_receive, ++ ath11k_nss_vdev_event_receive, ++ (struct net_device *)netdev->ieee80211_ptr, ++ features); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "failed to register nss mesh link vdev if_num %d nss_err:%d\n", ++ arvif->nss.if_num, status); ++ nss_unregister_wifi_vdev_if(arvif->nss.if_num); ++ return -EINVAL; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "registered nss mesh link vdev if_num %d\n", ++ arvif->nss.if_num); ++ ++ return 0; ++} ++#endif ++ + static int ath11k_nss_vdev_register(struct ath11k_vif *arvif, + struct net_device *netdev) + { +@@ -1140,6 +2379,15 @@ static int ath11k_nss_vdev_register(stru + arvif->nss.if_num); + + break; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ case NL80211_IFTYPE_MESH_POINT: ++ if (!ab->nss.mesh_nss_offload_enabled) ++ return -ENOTSUPP; ++ ++ if (ath11k_nss_mesh_vdev_register(arvif, netdev)) ++ return -EINVAL; ++ break; ++#endif + default: + ath11k_warn(ab, "unsupported interface type %d for nss vdev register\n", + arvif->vif->type); +@@ -1149,6 +2397,62 @@ static int ath11k_nss_vdev_register(stru + return 0; + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static void ath11k_nss_mesh_vdev_free(struct ath11k_vif *arvif) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct ath11k *ar = arvif->ar; ++ struct ath11k_nss_mpath_entry *mpath_entry, *mpath_tmp; ++ struct ath11k_nss_mpp_entry *mpp_entry, *mpp_tmp; ++ struct arvif_nss *nss = &arvif->nss, *nss_entry, *nss_tmp; ++ LIST_HEAD(mpath_local_entry); ++ LIST_HEAD(mpp_local_entry); ++ nss_tx_status_t status; ++ ++ del_timer_sync(&nss->mpp_expiry_timer); ++ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpath_dump, &mpath_local_entry); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(mpath_entry, mpath_tmp, &mpath_local_entry, list) { ++ list_del(&mpath_entry->list); ++ kfree(mpath_entry); ++ } ++ ++ spin_lock_bh(&ar->nss.dump_lock); ++ list_splice_tail_init(&nss->mpp_dump, &mpp_local_entry); ++ spin_unlock_bh(&ar->nss.dump_lock); ++ ++ list_for_each_entry_safe(mpp_entry, mpp_tmp, &mpp_local_entry, list) { ++ list_del(&mpp_entry->list); ++ kfree(mpp_entry); ++ } ++ ++ list_for_each_entry_safe(nss_entry, nss_tmp, &mesh_vaps, list) ++ list_del(&nss_entry->list); ++ ++ status = nss_dynamic_interface_dealloc_node( ++ arvif->nss.if_num, ++ NSS_DYNAMIC_INTERFACE_TYPE_VAP); ++ if (status != NSS_TX_SUCCESS) ++ ath11k_warn(ab, "failed to free nss mesh link vdev nss_err:%d\n", ++ status); ++ else ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "nss mesh link vdev interface deallocated\n"); ++ ++ status = (nss_tx_status_t)nss_wifi_meshmgr_if_destroy_sync(arvif->nss.mesh_handle); ++ ++ if (status != NSS_TX_SUCCESS) ++ ath11k_warn(ab, "failed to free nss mesh object vdev nss_err:%d\n", ++ status); ++ else ++ ath11k_dbg(ab, ATH11K_DBG_NSS, ++ "nss mesh object vdev interface deallocated\n"); ++} ++#endif ++ + void ath11k_nss_vdev_free(struct ath11k_vif *arvif) + { + struct ath11k_base *ab = arvif->ar->ab; +@@ -1168,6 +2472,11 @@ void ath11k_nss_vdev_free(struct ath11k_ + "nss vdev interface deallocated\n"); + + return; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ case NL80211_IFTYPE_MESH_POINT: ++ ath11k_nss_mesh_vdev_free(arvif); ++ return; ++#endif + default: + ath11k_warn(ab, "unsupported interface type %d for nss vdev dealloc\n", + arvif->vif->type); +@@ -1175,11 +2484,96 @@ void ath11k_nss_vdev_free(struct ath11k_ + } + } + +-static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif) ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++struct arvif_nss *ath11k_nss_find_arvif_by_if_num(int if_num) ++{ ++ struct arvif_nss *nss; ++ ++ list_for_each_entry(nss, &mesh_vaps, list) { ++ if (if_num == nss->if_num) ++ return nss; ++ } ++ return NULL; ++} ++ ++int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif, int if_num) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ struct ath11k_vif *arvif_link; ++ struct wireless_dev *wdev; ++ struct arvif_nss *nss; ++ int ret; ++ ++ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif); ++ if (!wdev) { ++ ath11k_warn(ab, "ath11k_nss: wdev is null\n"); ++ return -EINVAL; ++ } ++ ++ if (!wdev->netdev) { ++ ath11k_warn(ab, "ath11k_nss: netdev is null\n"); ++ return -EINVAL; ++ } ++ ++ nss = ath11k_nss_find_arvif_by_if_num(if_num); ++ if (!nss) { ++ ath11k_warn(ab, "ath11k_nss: unable to find if_num %d\n",if_num); ++ return -EINVAL; ++ } ++ ++ arvif_link = container_of(nss, struct ath11k_vif, nss); ++ ++ ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, ++ "assoc link vap ifnum %d to mesh handle of link id %d\n", ++ arvif_link->nss.if_num, arvif->nss.if_num); ++ ++ arvif_link->nss.mesh_handle = arvif->nss.mesh_handle; ++ ++ ret = ath11k_nss_mesh_obj_assoc_link_vap(arvif_link); ++ if (ret) ++ ath11k_warn(ab, "failed to associate link vap to mesh vap %d\n", ret); ++ ++ return 0; ++} ++ ++static int ath11k_nss_mesh_vdev_alloc(struct ath11k_vif *arvif, ++ struct net_device *netdev) ++{ ++ struct ath11k_base *ab = arvif->ar->ab; ++ int if_num; ++ ++ if (!ab->nss.mesh_nss_offload_enabled) ++ return -ENOTSUPP; ++ ++ if_num = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_VAP); ++ if (if_num < 0) { ++ ath11k_warn(ab, "failed to allocate nss mesh link vdev\n"); ++ return -EINVAL; ++ } ++ ++ arvif->nss.if_num = if_num; ++ ++ INIT_LIST_HEAD(&arvif->nss.list); ++ list_add_tail(&arvif->nss.list, &mesh_vaps); ++ ++ INIT_LIST_HEAD(&arvif->nss.mpath_dump); ++ init_completion(&arvif->nss.dump_mpath_complete); ++ INIT_LIST_HEAD(&arvif->nss.mpp_dump); ++ init_completion(&arvif->nss.dump_mpp_complete); ++ ++ return 0; ++} ++#endif ++ ++static int ath11k_nss_vdev_alloc(struct ath11k_vif *arvif, ++ struct net_device *netdev) + { + struct ath11k_base *ab = arvif->ar->ab; + enum nss_dynamic_interface_type if_type; + int if_num; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ int ret; ++#endif + + /* Initialize completion for verifying NSS message response */ + init_completion(&arvif->nss.complete); +@@ -1201,6 +2595,16 @@ static int ath11k_nss_vdev_alloc(struct + arvif->nss.if_num); + + break; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ case NL80211_IFTYPE_MESH_POINT: ++ ret = ath11k_nss_mesh_vdev_alloc(arvif, netdev); ++ if (ret) { ++ ath11k_warn(ab, "failed to allocate nss vdev of mesh type %d\n", ++ ret); ++ return ret; ++ } ++ break; ++#endif + default: + ath11k_warn(ab, "unsupported interface type %d for nss vdev alloc\n", + arvif->vif->type); +@@ -1238,7 +2642,7 @@ int ath11k_nss_vdev_create(struct ath11k + return -EINVAL; + } + +- ret = ath11k_nss_vdev_alloc(arvif); ++ ret = ath11k_nss_vdev_alloc(arvif, wdev->netdev); + if (ret) + return ret; + +@@ -1254,6 +2658,45 @@ int ath11k_nss_vdev_create(struct ath11k + goto unregister_vdev; + + break; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ case NL80211_IFTYPE_MESH_POINT: ++ ret = ath11k_nss_mesh_alloc_register(arvif, wdev->netdev); ++ if (ret) { ++ ath11k_warn(ab, "failed to alloc and register mesh vap %d\n", ret); ++ goto unregister_vdev; ++ } ++ ++ ret = ath11k_nss_vdev_configure(arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to configure nss mesh link vdev\n"); ++ goto unregister_vdev; ++ } ++ ++ ret = ath11k_nss_mesh_obj_assoc_link_vap(arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to associate link vap to mesh vap %d\n", ret); ++ goto unregister_vdev; ++ } ++ ++ ret = ath11k_nss_vdev_set_cmd(arvif, ++ ATH11K_NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD, 1); ++ if (ret) { ++ ath11k_warn(ab, "failed to enable mcast/bcast exception %d\n", ret); ++ goto unregister_vdev; ++ } ++ ++ ath11k_debugfs_nss_mesh_vap_create(arvif); ++ ++ /* This timer cb is called at specified ++ * interval to update mpp exp timeout */ ++ timer_setup(&arvif->nss.mpp_expiry_timer, ++ ath11k_nss_mpp_timer_cb, 0); ++ ++ /* Start the initial timer in 2 secs */ ++ mod_timer(&arvif->nss.mpp_expiry_timer, ++ jiffies + msecs_to_jiffies(2 * HZ)); ++ break; ++#endif + default: + ret = -ENOTSUPP; + goto unregister_vdev; +@@ -1310,6 +2753,15 @@ int ath11k_nss_vdev_up(struct ath11k_vif + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + return 0; + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT) { ++ status = (nss_tx_status_t)nss_wifi_meshmgr_if_up(arvif->nss.mesh_handle); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss mesh vdev up error %d\n", status); ++ return -EINVAL; ++ } ++ } ++#endif + vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); + if (!vdev_msg) + return -ENOMEM; +@@ -1357,6 +2809,15 @@ int ath11k_nss_vdev_down(struct ath11k_v + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + return 0; + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ if (arvif->vif->type == NL80211_IFTYPE_MESH_POINT) { ++ status = (nss_tx_status_t)nss_wifi_meshmgr_if_down(arvif->nss.mesh_handle); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ar->ab, "nss mesh vdev up error %d\n", status); ++ return -EINVAL; ++ } ++ } ++#endif + vdev_msg = kzalloc(sizeof(struct nss_wifi_vdev_msg), GFP_ATOMIC); + if (!vdev_msg) + return -ENOMEM; +@@ -2731,6 +4192,51 @@ static int ath11k_nss_get_dynamic_interf + } + } + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static int ath11k_nss_mesh_capability(struct ath11k_base *ab) ++{ ++ struct nss_wifili_msg *wlmsg = NULL; ++ nss_wifili_msg_callback_t msg_cb; ++ nss_tx_status_t status; ++ int ret = 0; ++ ++ wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); ++ if (!wlmsg) ++ return -ENOMEM; ++ ++ msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ ++ reinit_completion(&ab->nss.complete); ++ ++ nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ NSS_WIFILI_SEND_MESH_CAPABILITY_INFO, ++ sizeof(struct nss_wifili_mesh_capability_info), ++ msg_cb, NULL); ++ ++ status = nss_wifili_tx_msg(ab->nss.ctx, wlmsg); ++ if (status != NSS_TX_SUCCESS) { ++ ath11k_warn(ab, "nss failed to get mesh capability msg %d\n", status); ++ ret = -EINVAL; ++ goto free; ++ } ++ ++ ret = wait_for_completion_timeout(&ab->nss.complete, ++ msecs_to_jiffies(ATH11K_NSS_MSG_TIMEOUT_MS)); ++ if (!ret) { ++ ath11k_warn(ab, "timeout while waiting for mesh capability check\n"); ++ ret = -ETIMEDOUT; ++ goto free; ++ } ++ ++ kfree(wlmsg); ++ return 0; ++ ++free: ++ kfree(wlmsg); ++ return ret; ++} ++#endif ++ + static int ath11k_nss_init(struct ath11k_base *ab) + { + struct nss_wifili_init_msg *wim = NULL; +@@ -2863,6 +4369,17 @@ static int ath11k_nss_init(struct ath11k + + kfree(wlmsg); + ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ /* Create a mesh links read debugfs entry */ ++ ath11k_debugfs_nss_soc_create(ab); ++ ++ /* Check for mesh capability */ ++ ret = ath11k_nss_mesh_capability(ab); ++ ++ if (ret) ++ ath11k_err(ab, "Mesh offload is not enabled %d\n", ret); ++#endif ++ + ath11k_dbg(ab, ATH11K_DBG_NSS, "NSS Init Message TX Success %p %d\n", + ab->nss.ctx, ab->nss.if_num); + return 0; +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -10,8 +10,12 @@ + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + #include + #include +- ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++#include ++#endif + #endif ++#include "../../../../../net/mac80211/mesh.h" ++ + struct ath11k; + struct ath11k_base; + struct ath11k_vif; +@@ -23,8 +27,9 @@ struct hal_rx_user_status; + + /* NSS DBG macro is not included as part of debug enum to avoid + * frequent changes during upgrade*/ +-#define ATH11K_DBG_NSS 0x40000000 +-#define ATH11K_DBG_NSS_WDS 0x80000000 ++#define ATH11K_DBG_NSS 0x20000000 ++#define ATH11K_DBG_NSS_WDS 0x40000000 ++#define ATH11K_DBG_NSS_MESH 0x80000000 + + /* WIFILI Supported Target Types */ + #define ATH11K_WIFILI_TARGET_TYPE_UNKNOWN 0xFF +@@ -60,6 +65,7 @@ struct hal_rx_user_status; + /* Timeout for waiting for response from NSS on TX msg */ + #define ATH11K_NSS_MSG_TIMEOUT_MS 5000 + ++#define ATH11K_MESH_DEFAULT_ELEMENT_TTL 31 + /* Init Flags */ + #define WIFILI_NSS_CCE_DISABLED 0x1 + #define WIFILI_ADDTL_MEM_SEG_SET 0x000000002 +@@ -119,6 +125,8 @@ enum ath11k_nss_opmode { + ATH11K_NSS_OPMODE_MONITOR, + }; + ++#define ATH11K_MPP_EXPIRY_TIMER_INTERVAL_MS 60 * HZ ++ + struct peer_stats { + u64 last_rx; + u64 last_ack; +@@ -158,10 +166,30 @@ struct ath11k_nss_peer { + struct completion complete; + }; + ++struct ath11k_nss_mpath_entry { ++ struct list_head list; ++ u32 num_entries; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ struct nss_wifi_mesh_path_dump_entry mpath[0]; ++#endif ++}; ++ ++struct ath11k_nss_mpp_entry { ++ struct list_head list; ++ u32 num_entries; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ struct nss_wifi_mesh_proxy_path_dump_entry mpp[0]; ++#endif ++}; ++ + /* Structure to hold the vif related info for nss offload support */ + struct arvif_nss { + /* dynamic ifnum allocated by nss driver for vif */ + int if_num; ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++ /* mesh handle for mesh obj vap */ ++ nss_wifi_mesh_handle_t mesh_handle; ++#endif + /* Used for completion status for vdev config nss messages */ + struct completion complete; + /* Keep the copy of encap type for nss */ +@@ -183,6 +211,25 @@ struct arvif_nss { + /* WDS cfg should be done only once for ext vdev */ + bool wds_cfg_done; + bool created; ++ ++ bool mpp_aging; ++ bool mpp_dump_req; ++ struct timer_list mpp_expiry_timer; ++ u8 mesh_ttl; ++ bool mesh_forward_enabled; ++ u32 metadata_type; ++ u32 mpath_refresh_time; ++ ++ struct list_head list; ++ struct list_head mpath_dump; ++ /* total number of mpath entries in all of the mpath_dump list */ ++ u32 mpath_dump_num_entries; ++ struct completion dump_mpath_complete; ++ ++ struct list_head mpp_dump; ++ /* total number of mpp entries in all of the mpp_dump list */ ++ u32 mpp_dump_num_entries; ++ struct completion dump_mpp_complete; + }; + + /* Structure to hold the pdev/radio related info for nss offload support */ +@@ -191,6 +238,8 @@ struct ath11k_nss { + int if_num; + /* Radio/pdev Context obtained on pdev register */ + void* ctx; ++ /* protects stats from nss */ ++ spinlock_t dump_lock; + }; + + /* Structure to hold the soc related info for nss offload support */ +@@ -199,6 +248,8 @@ struct ath11k_soc_nss { + bool enabled; + /* turn on/off nss stats support in ath11k */ + bool stats_enabled; ++ /* Mesh offload support as advertised by nss */ ++ bool mesh_nss_offload_enabled; + /* soc nss ctx */ + void* ctx; + /* if_num to be used for soc related nss messages */ +@@ -261,6 +312,29 @@ void ath11k_nss_update_sta_rxrate(struct + int ath11k_nss_setup(struct ath11k_base *ab); + int ath11k_nss_teardown(struct ath11k_base *ab); + void ath11k_nss_ext_rx_stats(struct ath11k_base *ab, struct htt_rx_ring_tlv_filter *tlv_filter); ++int ath11k_nss_dump_mpath_request(struct ath11k_vif *arvif); ++int ath11k_nss_dump_mpp_request(struct ath11k_vif *arvif); ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++int ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path); ++#else ++static inline int ++ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ return 0; ++} ++#endif ++int ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed); ++int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif, int if_num); ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_exception_flag_msg *nss_msg); ++int ath11k_nss_exc_rate_config(struct ath11k_vif *arvif, ++ struct nss_wifi_mesh_rate_limit_config *nss_exc_cfg); ++#endif + #else + static inline int ath11k_nss_tx(struct ath11k_vif *arvif, struct sk_buff *skb) + { +@@ -431,5 +505,38 @@ static inline void ath11k_nss_ext_rx_sta + { + return; + } ++ ++#ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT ++static inline int ++ath11k_nss_mesh_config_path(struct ath11k *ar, struct ath11k_vif *arvif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ return 0; ++} ++#endif ++static inline int ++ath11k_nss_mesh_config_update(struct ieee80211_vif *vif, int changed) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_assoc_link_arvif_to_ifnum(struct ath11k_vif *arvif, ++ int if_num) ++{ ++ return 0; ++} ++ ++static inline int ath11k_nss_mesh_exception_flags(struct ath11k_vif *arvif, ++ void *nss_msg) ++{ ++ return 0; ++} ++ ++static inline int ++ath11k_nss_exc_rate_config(struct ath11k_vif *arvif, void *nss_exc_cfg) ++{ ++ return 0; ++} + #endif /* CPTCFG_ATH11K_NSS_SUPPORT */ + #endif +--- a/drivers/net/wireless/ath/ath11k/debug.h ++++ b/drivers/net/wireless/ath/ath11k/debug.h +@@ -10,6 +10,7 @@ + #include "trace.h" + #include "debugfs.h" + ++extern struct dentry *debugfs_ath11k; + enum ath11k_debug_mask { + ATH11K_DBG_AHB = 0x00000001, + ATH11K_DBG_WMI = 0x00000002, +--- a/local-symbols ++++ b/local-symbols +@@ -171,6 +171,7 @@ ATH11K= + ATH11K_AHB= + ATH11K_PCI= + ATH11K_NSS_SUPPORT= ++ATH11K_NSS_MESH_SUPPORT= + ATH11K_MEM_PROFILE_256M= + ATH11K_MEM_PROFILE_512M= + ATH11K_DEBUG= +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -23,6 +23,15 @@ config ATH11K_NSS_SUPPORT + + If unsure, say Y to enable NSS offload support. + ++config ATH11K_NSS_MESH_SUPPORT ++ bool "QCA ath11k nss mesh support" ++ depends on ATH11K_NSS_SUPPORT ++ default n ++ ---help--- ++ Enables NSS offload support for ATH11K Mesh ++ ++ If unsure, say Y to enable NSS offload support. ++ + config ATH11K_MEM_PROFILE_512M + bool "ath11k enable 512MB memory profile" + depends on ATH11K diff --git a/package/kernel/mac80211/patches/nss/ath11k/301-ath11k-nss-mcbc-exception.patch b/package/kernel/mac80211/patches/nss/ath11k/301-ath11k-nss-mcbc-exception.patch new file mode 100644 index 00000000000000..2fa70d0f395ab3 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/301-ath11k-nss-mcbc-exception.patch @@ -0,0 +1,203 @@ +From 91df8aa674d2d4064ab22f47515c3fb126527208 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Thu, 12 Nov 2020 15:02:56 +0530 +Subject: [PATCH] ath11k: NSS MCBC Exception added for STA + +Since NSS FW is not supporting PN check for MCBC pkts, those pkts are +excepted from NSS offload to pass through mac80211 PN check. + +Signed-off-by: Karthikeyan Kathirvel +Change-Id: I4a6ac67a1c2cf3ab7a219d0953907191606a5e70 +--- + drivers/net/wireless/ath/ath11k/nss.c | 128 +++++++++++++++++++++ + drivers/net/wireless/ath/ath11k/nss.h | 51 ++++---- + 2 files changed, 153 insertions(+), 26 deletions(-) + create mode 100644 mac80211/patches/301-ath11k-nss-mcbc-exception.patch + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -606,7 +606,7 @@ static int ath11k_nss_undecap_nwifi(stru + + static void ath11k_nss_wds_type_rx(struct ath11k *ar, struct net_device *dev, + u8* src_mac, u8 is_sa_valid, u8 addr4_valid, +- u16 peer_id, bool *drop) ++ u16 peer_id) + { + struct ath11k_base *ab = ar->ab; + struct ath11k_ast_entry *ast_entry = NULL; +@@ -642,8 +642,6 @@ static void ath11k_nss_wds_type_rx(struc + } + } + +- if (!ta_peer->nss.ext_vdev_up) +- *drop = true; + } + + spin_unlock_bh(&ab->base_lock); +@@ -687,8 +685,7 @@ static void ath11k_nss_mec_handler(struc + + static void ath11k_nss_vdev_spl_receive_ext_wdsdata(struct ath11k_vif *arvif, + struct sk_buff *skb, +- struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata, +- bool *drop) ++ struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata) + { + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; +@@ -710,7 +707,7 @@ static void ath11k_nss_vdev_spl_receive_ + switch (wds_type) { + case NSS_WIFI_VDEV_WDS_TYPE_RX: + ath11k_nss_wds_type_rx(ar, skb->dev, src_mac, is_sa_valid, +- addr4_valid, peer_id, drop); ++ addr4_valid, peer_id); + break; + case NSS_WIFI_VDEV_WDS_TYPE_MEC: + ath11k_nss_mec_handler(ar, (u8 *)(skb->data)); +@@ -775,10 +772,12 @@ ath11k_nss_vdev_special_data_receive(str + struct nss_wifi_vdev_wds_per_packet_metadata *wds_metadata = NULL; + struct ath11k_vif *arvif; + struct ath11k_base *ab; +- bool drop = false; + bool eth_decap = false; + int data_offs = 0; + int ret = 0; ++ struct nss_wifi_vdev_addr4_data_metadata *addr4_metadata = NULL; ++ struct ath11k_skb_rxcb *rxcb; ++ struct ath11k_peer *ta_peer = NULL; + + arvif = ath11k_nss_get_arvif_from_dev(dev); + if (!arvif) { +@@ -810,15 +809,50 @@ ath11k_nss_vdev_special_data_receive(str + return; + } + +- if (eth_decap && wifi_metadata->pkt_type == +- NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_WDS_LEARN) { +- wds_metadata = &wifi_metadata->metadata.wds_metadata; +- ath11k_nss_vdev_spl_receive_ext_wdsdata(arvif, skb, +- wds_metadata, &drop); +- } ++ switch(wifi_metadata->pkt_type) { ++ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_WDS_LEARN: ++ if (eth_decap) { ++ wds_metadata = &wifi_metadata->metadata.wds_metadata; ++ ath11k_nss_vdev_spl_receive_ext_wdsdata(arvif, skb, ++ wds_metadata); ++ } ++ dev_kfree_skb_any(skb); ++ break; ++ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_MCBC_RX: ++ ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", ++ "mcbc packet exception from nss: ", ++ skb->data, skb->len); ++ rxcb = ATH11K_SKB_RXCB(skb); ++ rxcb->rx_desc = (struct hal_rx_desc *)skb->head; ++ rxcb->is_first_msdu = rxcb->is_last_msdu = true; ++ rxcb->is_continuation = false; ++ rxcb->is_mcbc = true; ++ ath11k_dp_rx_from_nss(arvif->ar, skb, napi); ++ break; ++ case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_4ADDR: ++ if (eth_decap) { ++ addr4_metadata = &wifi_metadata->metadata.addr4_metadata; ++ ++ spin_lock_bh(&ab->base_lock); ++ ta_peer = ath11k_peer_find_by_id(ab, addr4_metadata->peer_id); ++ if (!ta_peer) { ++ spin_unlock_bh(&ab->base_lock); ++ dev_kfree_skb_any(skb); ++ return; ++ } + +- if (!drop) +- ath11k_nss_deliver_rx(arvif->vif, skb, eth_decap, data_offs, napi); ++ ath11k_dbg(ab, ATH11K_DBG_NSS_WDS, "4addr exception ta_peer %pM\n", ++ ta_peer->addr); ++ if (!ta_peer->nss.ext_vdev_up && addr4_metadata->addr4_valid) ++ ieee80211_rx_nss_notify_4addr(dev, ta_peer->addr); ++ spin_unlock_bh(&ab->base_lock); ++ } ++ dev_kfree_skb_any(skb); ++ break; ++ default: ++ ath11k_warn(ab, "unsupported pkt_type %d from nss\n", wifi_metadata->pkt_type); ++ dev_kfree_skb_any(skb); ++ } + } + + static void +@@ -2129,6 +2163,9 @@ int ath11k_nss_vdev_set_cmd(struct ath11 + case ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD: + cmd = NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD; + break; ++ case ATH11K_NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD: ++ cmd = NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD; ++ break; + default: + return -EINVAL; + } +@@ -2651,12 +2688,31 @@ int ath11k_nss_vdev_create(struct ath11k + goto free_vdev; + + switch (arvif->vif->type) { +- case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_STATION: + ret = ath11k_nss_vdev_configure(arvif); + if (ret) + goto unregister_vdev; + ++ ret = ath11k_nss_vdev_set_cmd(arvif, ++ ATH11K_NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD, ++ ATH11K_NSS_ENABLE_MCBC_EXC); ++ if (ret) { ++ ath11k_err(ab, "failed to set MCBC in nss %d\n", ret); ++ goto unregister_vdev; ++ } ++ break; ++ case NL80211_IFTYPE_AP: ++ ret = ath11k_nss_vdev_configure(arvif); ++ if (ret) ++ goto unregister_vdev; ++ ++ ret = ath11k_nss_vdev_set_cmd(arvif, ++ ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD, ++ true); ++ if (ret) { ++ ath11k_warn(ab, "failed to cfg wds backhaul in nss %d\n", ret); ++ goto unregister_vdev; ++ } + break; + #ifdef CPTCFG_ATH11K_NSS_MESH_SUPPORT + case NL80211_IFTYPE_MESH_POINT: +@@ -2987,7 +3043,6 @@ static int ath11k_nss_ext_vdev_register( + { + struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; +- nss_tx_status_t status; + u32 features = 0; + + if (arvif->vif->type != NL80211_IFTYPE_AP_VLAN || arvif->nss.ctx) +@@ -3001,7 +3056,7 @@ static int ath11k_nss_ext_vdev_register( + + if (!arvif->nss.ctx) { + ath11k_warn(ab, "failed to register nss vdev if_num %d nss_err:%d\n", +- arvif->nss.if_num, status); ++ arvif->nss.if_num, NSS_TX_FAILURE); + return -EINVAL; + } + +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -115,8 +115,12 @@ enum ath11k_nss_vdev_cmd { + ATH11K_NSS_WIFI_VDEV_ENCAP_TYPE_CMD, + ATH11K_NSS_WIFI_VDEV_DECAP_TYPE_CMD, + ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD, ++ ATH11K_NSS_WIFI_VDEV_CFG_MCBC_EXC_TO_HOST_CMD, + }; + ++/* Enables the MCBC exception in NSS fw, 1 = enable */ ++#define ATH11K_NSS_ENABLE_MCBC_EXC 1 ++ + enum ath11k_nss_opmode { + ATH11K_NSS_OPMODE_UNKNOWN, + ATH11K_NSS_OPMODE_AP, diff --git a/package/kernel/mac80211/patches/nss/ath11k/314-ath11k-Fix-peer-lookup-failure-in-mgmt-tx-completion.patch b/package/kernel/mac80211/patches/nss/ath11k/314-ath11k-Fix-peer-lookup-failure-in-mgmt-tx-completion.patch new file mode 100644 index 00000000000000..6edfd5f9208e97 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/314-ath11k-Fix-peer-lookup-failure-in-mgmt-tx-completion.patch @@ -0,0 +1,125 @@ +From e58249f0a5826926c0e96acea4dfbc8683cfaaab Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Wed, 9 Jun 2021 17:32:30 +0530 +Subject: [PATCH] ath11k: Fix peer lookup failure in mgmt tx completion + +In mgmt tx completion handler, peer lookup is done using address 2 +of transmitted frame to find arvif and update mgmt tx completion stats. +For STA interface, self peer will not be created and hence +peer lookup with address 2 keeps failing. +Fix this by obtaining vif directly from SKB_CB for updating stats. + +Possible vif removal races: +1. If vif removed before tx completion all idrs associated to the vif +would've been flushed and finding msdu with idr will fail, +hence tx completion wont be processed therafter. +2. Added data lock to protect vif removal during tx completion processing. + +Signed-off-by: Rameshkumar Sundaram +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 ++ + drivers/net/wireless/ath/ath11k/wmi.c | 53 ++++++++++++++++------------------- + 2 files changed, 26 insertions(+), 29 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7638,8 +7638,10 @@ err_vdev_del: + kfree(arvif->vlan_keyid_map); + ath11k_peer_cleanup(ar, arvif->vdev_id); + ++ spin_lock_bh(&ar->data_lock); + idr_for_each(&ar->txmgmt_idr, + ath11k_mac_vif_txmgmt_idr_remove, vif); ++ spin_unlock_bh(&ar->data_lock); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + spin_lock_bh(&ab->dp.tx_ring[i].tx_idr_lock); +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -5966,13 +5966,13 @@ static int wmi_process_tx_comp(struct at + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + struct ieee80211_hdr *hdr; +- struct ath11k_peer *peer; + struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_mgmt_frame_stats *mgmt_stats; + u16 frm_type; + int num_mgmt; + ++ spin_lock_bh(&ar->data_lock); + spin_lock_bh(&ar->txmgmt_idr_lock); + msdu = idr_find(&ar->txmgmt_idr, tx_compl_param->desc_id); + +@@ -5980,6 +5980,7 @@ static int wmi_process_tx_comp(struct at + ath11k_warn(ar->ab, "received mgmt tx compl for invalid msdu_id: %d\n", + tx_compl_param->desc_id); + spin_unlock_bh(&ar->txmgmt_idr_lock); ++ spin_unlock_bh(&ar->data_lock); + return -ENOENT; + } + +@@ -5988,6 +5989,28 @@ static int wmi_process_tx_comp(struct at + + skb_cb = ATH11K_SKB_CB(msdu); + dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); ++ hdr = (struct ieee80211_hdr *)msdu->data; ++ ++ if (ieee80211_is_mgmt(hdr->frame_control)) { ++ frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); ++ vif = skb_cb->vif; ++ ++ if (!vif) { ++ ath11k_warn(ar->ab, "failed to find vif to update txcompl mgmt stats\n"); ++ goto skip_mgmt_stats; ++ } ++ ++ arvif = ath11k_vif_to_arvif(vif); ++ mgmt_stats = &arvif->mgmt_stats; ++ ++ if (!tx_compl_param->status) ++ mgmt_stats->tx_compl_succ[frm_type]++; ++ else ++ mgmt_stats->tx_compl_fail[frm_type]++; ++ } ++ ++skip_mgmt_stats: ++ spin_unlock_bh(&ar->data_lock); + + info = IEEE80211_SKB_CB(msdu); + if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && +@@ -6003,34 +6026,6 @@ static int wmi_process_tx_comp(struct at + */ + info->status.rates[0].idx = -1; + +- hdr = (struct ieee80211_hdr *)msdu->data; +- frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); +- +- spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2); +- if (!peer) { +- spin_unlock_bh(&ar->ab->base_lock); +- ath11k_warn(ar->ab, "failed to find peer to update txcompl mgmt stats\n"); +- goto skip_mgmt_stats; +- } +- +- vif = peer->vif; +- spin_unlock_bh(&ar->ab->base_lock); +- +- spin_lock_bh(&ar->data_lock); +- arvif = ath11k_vif_to_arvif(vif); +- mgmt_stats = &arvif->mgmt_stats; +- +- if (ieee80211_is_mgmt(hdr->frame_control)) { +- if (!tx_compl_param->status) +- mgmt_stats->tx_compl_succ[frm_type]++; +- else +- mgmt_stats->tx_compl_fail[frm_type]++; +- } +- +- spin_unlock_bh(&ar->data_lock); +- +-skip_mgmt_stats: + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); diff --git a/package/kernel/mac80211/patches/nss/ath11k/318-ath11k-avoid-stack-corrupt-in-nwifi-undecap.patch b/package/kernel/mac80211/patches/nss/ath11k/318-ath11k-avoid-stack-corrupt-in-nwifi-undecap.patch new file mode 100644 index 00000000000000..9189729b77f595 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/318-ath11k-avoid-stack-corrupt-in-nwifi-undecap.patch @@ -0,0 +1,11 @@ +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -9,7 +9,7 @@ + #include "rx_desc.h" + #include "debug.h" + +-#define DP_MAX_NWIFI_HDR_LEN 30 ++#define DP_MAX_NWIFI_HDR_LEN 36 + + #define DP_RX_MPDU_ERR_FCS BIT(0) + #define DP_RX_MPDU_ERR_DECRYPT BIT(1) diff --git a/package/kernel/mac80211/patches/nss/ath11k/319-ath11k-fix-double-free-of-peer-rx_tid-during-reo-cmd.patch b/package/kernel/mac80211/patches/nss/ath11k/319-ath11k-fix-double-free-of-peer-rx_tid-during-reo-cmd.patch new file mode 100644 index 00000000000000..d733219b38aa7f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/319-ath11k-fix-double-free-of-peer-rx_tid-during-reo-cmd.patch @@ -0,0 +1,40 @@ +From 210b20357989ace8bb5c861b37e25b6c986c816d Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Thu, 1 Jul 2021 13:08:54 +0530 +Subject: [PATCH] ath11k: fix double free of peer rx_tid during reo cmd failure + +Peer rx_tid is locally copied thrice during peer_rx_tid_cleanup to send +REO_CMD_UPDATE_RX_QUEUE followed by REO_CMD_FLUSH_CACHE to flush all +aged REO descriptors from HW cache. + +When sending REO_CMD_FLUSH_CACHE fails, we do dma unmap of already +mapped rx_tid->vaddr and free it. This is not checked during +reo_cmd_list_cleanup() and dp_reo_cmd_free() before trying to free and +unmap again. + +Fix this by setting rx_tid->vaddr NULL in rx tid delete and also wherever +freeing it to check in reo_cmd_list_cleanup() and reo_cmd_free() before +trying to free again. + +Prevent REO cmd failures causing double free by increasing REO cmd +ring size and moving REO status ring mask to IRQ group 3 from group +0 to separate from tx completion ring on IRQ group 0 which may delay +reo status processing. + +Signed-off-by: Sathishkumar Muruganandam +--- + drivers/net/wireless/ath/ath11k/dp.h | 2 +- + drivers/net/wireless/ath/ath11k/dp_rx.c | 66 ++++++++++++++++++------- + drivers/net/wireless/ath/ath11k/hw.c | 1 + + 3 files changed, 49 insertions(+), 20 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -1332,6 +1332,7 @@ const struct ath11k_hw_ring_mask ath11k_ + ATH11K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { ++ 0, 0, 0, + ATH11K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { diff --git a/package/kernel/mac80211/patches/nss/ath11k/330-ath11k-sync-wds_ast_entry-updates.patch b/package/kernel/mac80211/patches/nss/ath11k/330-ath11k-sync-wds_ast_entry-updates.patch new file mode 100644 index 00000000000000..01ccc6003fc0cf --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/330-ath11k-sync-wds_ast_entry-updates.patch @@ -0,0 +1,523 @@ +From 9e1f28f343347774b01f330d76d2c5323fcd07ae Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Fri, 24 Sep 2021 18:25:08 +0530 +Subject: [PATCH] ath11k: sync wds ast entry on peer/entry deletions + +Peer specific ast entries list is shared between +peer_unmap_event(softirq) and peer_ast_wds_wmi_wk +(worker). The worker sends a WMI command and hence tends to +sleep. +complete_work_sync() is used in peer_unmap_event bh handler +which tries to yield the CPU(calls schedule) +until the worker completes. This results kernel reporting +schedule in atomic context and system crash. +Add new global wmi_ast_list and add all ast add/update +work to this list. +Add a new global wmi_ast_work and queue this +work on each entry to global wmi_ast_list list. +Process the wmi_ast_list in worker and send updates +to FW. +Each ast entry node is shared between global wmi_ast list +and peer specific ast_list and deletes will be done +in sync between two lists. +On peer deletion all the peer specific entries will +be deleted from wmi_ast_list and peer delete in progress +will be set to avoid new ast entries adding up and therefore +peer unmap evnt won't find any ast worker in progress +entries to free in irq context. +This peer ast cleanup and worker processing same entry +will be synchronized with new base_ast_lock mutex. +FW indpendently Deleting a single wds ast entry in irq context & +worker already processing the same cannot be synchronized. +As we can't hold bh scheduling while worker tries to sleep, +and entry in scheduled update work will be sent to FW. + +Signed-off-by: Rameshkumar Sundaram +--- + drivers/net/wireless/ath/ath11k/ahb.c | 1 + + drivers/net/wireless/ath/ath11k/core.c | 3 + + drivers/net/wireless/ath/ath11k/core.h | 4 + + drivers/net/wireless/ath/ath11k/nss.c | 6 +- + drivers/net/wireless/ath/ath11k/nss.h | 8 +- + drivers/net/wireless/ath/ath11k/pci.c | 1 + + drivers/net/wireless/ath/ath11k/peer.c | 172 ++++++++++++++++++++++++--------- + drivers/net/wireless/ath/ath11k/peer.h | 3 +- + 8 files changed, 144 insertions(+), 54 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -1245,6 +1245,7 @@ static void ath11k_ahb_remove_prepare(st + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + cancel_work_sync(&ab->qmi.event_work); ++ cancel_work_sync(&ab->wmi_ast_work); + } + + static void ath11k_ahb_free_resources(struct ath11k_base *ab) +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -2211,6 +2211,7 @@ struct ath11k_base *ath11k_core_alloc(st + + mutex_init(&ab->core_lock); + mutex_init(&ab->tbl_mtx_lock); ++ mutex_init(&ab->base_ast_lock); + spin_lock_init(&ab->base_lock); + mutex_init(&ab->vdev_id_11d_lock); + init_completion(&ab->reset_complete); +@@ -2224,6 +2225,8 @@ struct ath11k_base *ath11k_core_alloc(st + INIT_WORK(&ab->restart_work, ath11k_core_restart); + INIT_WORK(&ab->update_11d_work, ath11k_update_11d); + INIT_WORK(&ab->reset_work, ath11k_core_reset); ++ INIT_WORK(&ab->wmi_ast_work, ath11k_peer_ast_wds_wmi_wk); ++ INIT_LIST_HEAD(&ab->wmi_ast_list); + timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); + init_completion(&ab->htc_suspend); + init_completion(&ab->wow.wakeup_completed); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -31,6 +31,7 @@ + #include "wow.h" + #include "rx_desc.h" + #include "nss.h" ++#include "peer.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -1080,6 +1081,9 @@ struct ath11k_base { + + u32 max_ast_index; + u32 num_ast_entries; ++ struct mutex base_ast_lock; ++ struct work_struct wmi_ast_work; ++ struct list_head wmi_ast_list; + + bool stats_disable; + /* must be last */ +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -647,8 +647,9 @@ static void ath11k_nss_wds_type_rx(struc + spin_unlock_bh(&ab->base_lock); + } + +-static void ath11k_nss_mec_handler(struct ath11k *ar, u8* mec_mac_addr) ++static void ath11k_nss_mec_handler(struct ath11k_vif *arvif, u8* mec_mac_addr) + { ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_peer *peer = ar->bss_peer; + u8 mac_addr[ETH_ALEN]; +@@ -675,7 +676,7 @@ static void ath11k_nss_mec_handler(struc + memcpy(mac_addr, mac_addr_h16, ETH_ALEN - 4); + memcpy(mac_addr + 2, mac_addr_l32, 4); + +- if (!ether_addr_equal(ar->mac_addr, mac_addr)) { ++ if (!ether_addr_equal(arvif->vif->addr, mac_addr)) { + spin_lock_bh(&ab->base_lock); + ath11k_peer_add_ast(ar, peer, mac_addr, + ATH11K_AST_TYPE_MEC); +@@ -710,7 +711,7 @@ static void ath11k_nss_vdev_spl_receive_ + addr4_valid, peer_id); + break; + case NSS_WIFI_VDEV_WDS_TYPE_MEC: +- ath11k_nss_mec_handler(ar, (u8 *)(skb->data)); ++ ath11k_nss_mec_handler(arvif, (u8 *)(skb->data)); + break; + default: + ath11k_warn(ab, "unsupported wds_type %d\n", wds_type); +@@ -3796,11 +3797,7 @@ int ath11k_nss_add_wds_peer(struct ath11 + wds_peer_msg->ast_type = type; + wds_peer_msg->peer_id = peer->peer_id; + +- if (type == ATH11K_AST_TYPE_MEC) +- ether_addr_copy(wds_peer_msg->peer_mac, ar->mac_addr); +- else +- ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); +- ++ ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); + ether_addr_copy(wds_peer_msg->dest_mac, dest_mac); + + msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; +@@ -3923,7 +3920,7 @@ msg_free: + return ret; + } + +-int ath11k_nss_del_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, ++int ath11k_nss_del_wds_peer(struct ath11k *ar, u8 *peer_addr, int peer_id, + u8 *dest_mac) + { + struct ath11k_base *ab = ar->ab; +@@ -3941,8 +3938,8 @@ int ath11k_nss_del_wds_peer(struct ath11 + + wds_peer_msg->pdev_id = ar->pdev->pdev_id; + wds_peer_msg->ast_type = ATH11K_AST_TYPE_NONE; +- wds_peer_msg->peer_id = peer->peer_id; +- ether_addr_copy(wds_peer_msg->peer_mac, peer->addr); ++ wds_peer_msg->peer_id = peer_id; ++ ether_addr_copy(wds_peer_msg->peer_mac, peer_addr); + ether_addr_copy(wds_peer_msg->dest_mac, dest_mac); + + msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -290,8 +290,8 @@ int ath11k_nss_update_wds_peer(struct at + u8 *dest_mac); + int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, + u8 *dest_mac, enum ath11k_ast_entry_type type); +-int ath11k_nss_del_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, +- u8 *dest_mac); ++int ath11k_nss_del_wds_peer(struct ath11k *ar, u8 *peer_addr, ++ int peer_id, u8 *dest_mac); + int ath11k_nss_ext_vdev_cfg_wds_peer(struct ath11k_vif *arvif, + u8 *wds_addr, u32 wds_peer_id); + int ath11k_nss_ext_vdev_wds_4addr_allow(struct ath11k_vif *arvif, +@@ -413,8 +413,8 @@ static inline int ath11k_nss_map_wds_pee + return 0; + } + +-static inline int ath11k_nss_del_wds_peer(struct ath11k_vif *arvif, struct ath11k_peer *peer, +- u8 *dest_mac) ++static inline int ath11k_nss_del_wds_peer(struct ath11k *ar, u8 *peer_addr, ++ int peer_id, u8 *dest_mac) + { + return 0; + } +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -972,6 +972,7 @@ static void ath11k_pci_remove(struct pci + } + + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); ++ cancel_work_sync(&ab->wmi_ast_work); + + ath11k_core_deinit(ab); + +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -160,49 +160,68 @@ struct ath11k_ast_entry *ath11k_peer_ast + + void ath11k_peer_ast_wds_wmi_wk(struct work_struct *wk) + { +- struct ath11k_ast_entry *ast_entry = container_of(wk, +- struct ath11k_ast_entry, +- wds_wmi_wk); +- struct ath11k *ar; ++ struct ath11k_ast_entry *ast_entry, *entry; ++ struct ath11k_base *ab = container_of(wk, struct ath11k_base, wmi_ast_work); + struct ath11k_peer *peer; ++ struct ath11k *ar; + int ret; ++ u8 peer_addr[ETH_ALEN]; ++ int peer_id; + +- if (!ast_entry) +- return; ++ ast_entry = kzalloc(sizeof(*ast_entry), GFP_ATOMIC); + +- ar = ast_entry->ar; +- peer = ast_entry->peer; ++ mutex_lock(&ab->base_ast_lock); ++ spin_lock_bh(&ab->base_lock); ++ ++ while ((entry = list_first_entry_or_null(&ab->wmi_ast_list, ++ struct ath11k_ast_entry, wmi_list))) { ++ list_del_init(&entry->wmi_list); + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "ath11k_peer_ast_wds_wmi_wk action %d ast_entry %pM next_node %pM vdev %d\n", +- ast_entry->action, ast_entry->addr, ast_entry->next_node_mac, +- ast_entry->vdev_id); +- +- if (ast_entry->action == ATH11K_WDS_WMI_ADD) { +- ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, +- ast_entry->next_node_mac, +- ast_entry->addr, +- ast_entry->vdev_id, +- true); +- if (ret) { +- ath11k_warn(ar->ab, "add wds_entry_cmd failed %d for %pM next_node %pM\n", +- ret, ast_entry->addr, +- ast_entry->next_node_mac); +- if (peer) +- ath11k_nss_del_wds_peer(ar, peer, +- ast_entry->addr); ++ if (!entry->ar || (entry->peer && entry->peer->delete_in_progress)) { ++ continue; + } +- } else if (ast_entry->action == ATH11K_WDS_WMI_UPDATE) { +- if (!peer) +- return; ++ memcpy(ast_entry, entry, sizeof(*ast_entry)); ++ ar = ast_entry->ar; ++ peer = ast_entry->peer; ++ memcpy(peer_addr, peer->addr, sizeof(peer_addr)); ++ peer_id = peer->peer_id; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "ath11k_peer_ast_wds_wmi_wk action %d ast_entry %pM peer %pM vdev %d\n", ++ ast_entry->action, ast_entry->addr, peer_addr, ++ ast_entry->vdev_id); + +- ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, peer->addr, +- ast_entry->addr, +- ast_entry->vdev_id, +- false); +- if (ret) +- ath11k_warn(ar->ab, "update wds_entry_cmd failed %d for %pM on peer %pM\n", +- ret, ast_entry->addr, peer->addr); ++ if (ast_entry->action == ATH11K_WDS_WMI_ADD) { ++ spin_unlock_bh(&ab->base_lock); ++ ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, peer_addr, ++ ast_entry->addr, ++ ast_entry->vdev_id, ++ true); ++ if (ret) { ++ ath11k_warn(ar->ab, "add wds_entry_cmd failed %d for %pM, peer %pM\n", ++ ret, ast_entry->addr, peer_addr); ++ if (peer) ++ ath11k_nss_del_wds_peer(ar, peer_addr, peer_id, ++ ast_entry->addr); ++ } ++ } else if (ast_entry->action == ATH11K_WDS_WMI_UPDATE) { ++ if (!peer) { ++ continue; ++ } ++ spin_unlock_bh(&ab->base_lock); ++ ret = ath11k_wmi_send_add_update_wds_entry_cmd(ar, peer_addr, ++ ast_entry->addr, ++ ast_entry->vdev_id, ++ false); ++ if (ret) ++ ath11k_warn(ar->ab, "update wds_entry_cmd failed %d for %pM on peer %pM\n", ++ ret, ast_entry->addr, peer_addr); ++ } ++ spin_lock_bh(&ab->base_lock); + } ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ab->base_ast_lock); ++ kfree(ast_entry); + } + + int ath11k_peer_add_ast(struct ath11k *ar, struct ath11k_peer *peer, +@@ -211,6 +230,8 @@ int ath11k_peer_add_ast(struct ath11k *a + struct ath11k_ast_entry *ast_entry = NULL; + struct ath11k_base *ab = ar->ab; + ++ lockdep_assert_held(&ab->base_lock); ++ + if (ab->num_ast_entries == ab->max_ast_index) { + ath11k_warn(ab, "failed to add ast for %pM due to insufficient ast entry resource %d in target\n", + mac_addr, ab->max_ast_index); +@@ -226,6 +247,9 @@ int ath11k_peer_add_ast(struct ath11k *a + } + } + ++ if (peer && peer->delete_in_progress) ++ return -EINVAL; ++ + ast_entry = kzalloc(sizeof(*ast_entry), GFP_ATOMIC); + if (!ast_entry) { + ath11k_warn(ab, "failed to alloc ast_entry for %pM\n", +@@ -257,7 +281,7 @@ int ath11k_peer_add_ast(struct ath11k *a + } + + INIT_LIST_HEAD(&ast_entry->ase_list); +- INIT_WORK(&ast_entry->wds_wmi_wk, ath11k_peer_ast_wds_wmi_wk); ++ INIT_LIST_HEAD(&ast_entry->wmi_list); + ast_entry->vdev_id = peer->vdev_id; + ast_entry->pdev_idx = peer->pdev_idx; + ast_entry->is_mapped = false; +@@ -271,16 +295,12 @@ int ath11k_peer_add_ast(struct ath11k *a + ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_add_ast peer %pM ast_entry %pM, ast_type %d\n", + peer->addr, mac_addr, ast_entry->type); + +- if (type == ATH11K_AST_TYPE_MEC) +- ether_addr_copy(ast_entry->next_node_mac, ar->mac_addr); +- else if (type == ATH11K_AST_TYPE_WDS) +- ether_addr_copy(ast_entry->next_node_mac, peer->addr); +- + if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || + (ast_entry->type == ATH11K_AST_TYPE_MEC)) { + ath11k_nss_add_wds_peer(ar, peer, mac_addr, ast_entry->type); + ast_entry->action = ATH11K_WDS_WMI_ADD; +- ieee80211_queue_work(ar->hw, &ast_entry->wds_wmi_wk); ++ list_add_tail(&ast_entry->wmi_list, &ab->wmi_ast_list); ++ ieee80211_queue_work(ar->hw, &ab->wmi_ast_work); + } + + ab->num_ast_entries++; +@@ -293,6 +313,8 @@ int ath11k_peer_update_ast(struct ath11k + struct ath11k_peer *old_peer = ast_entry->peer; + struct ath11k_base *ab = ar->ab; + ++ lockdep_assert_held(&ab->base_lock); ++ + if (!ast_entry->is_mapped) { + ath11k_warn(ab, "ath11k_peer_update_ast: ast_entry %pM not mapped yet\n", + ast_entry->addr); +@@ -305,6 +327,9 @@ int ath11k_peer_update_ast(struct ath11k + (ast_entry->is_active)) + return 0; + ++ if (peer && peer->delete_in_progress) ++ return -EINVAL; ++ + ast_entry->vdev_id = peer->vdev_id; + ast_entry->pdev_idx = peer->pdev_idx; + ast_entry->type = ATH11K_AST_TYPE_WDS; +@@ -317,7 +342,14 @@ int ath11k_peer_update_ast(struct ath11k + old_peer->addr, peer->addr, ast_entry->addr); + + ast_entry->action = ATH11K_WDS_WMI_UPDATE; +- ieee80211_queue_work(ar->hw, &ast_entry->wds_wmi_wk); ++ ++ /* wmi_list entry might've been processed & removed.*/ ++ if (list_empty(&ast_entry->wmi_list)) ++ list_add_tail(&ast_entry->wmi_list, &ab->wmi_ast_list); ++ else ++ list_move_tail(&ast_entry->wmi_list, &ab->wmi_ast_list); ++ ++ ieee80211_queue_work(ar->hw, &ab->wmi_ast_work); + + return 0; + } +@@ -363,16 +395,23 @@ void ath11k_peer_del_ast(struct ath11k * + ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_del_ast pdev:%d peer %pM ast_entry %pM\n", + ar->pdev->pdev_id, peer->addr, ast_entry->addr); + +- if (ast_entry->is_mapped) +- list_del(&ast_entry->ase_list); ++ if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || ++ (ast_entry->type == ATH11K_AST_TYPE_MEC)) { ++ if (!list_empty(&ast_entry->wmi_list)) { ++ ath11k_dbg(ab, ATH11K_DBG_MAC, ++ "ath11k_peer_del_ast deleting unprocessed ast entry %pM " ++ "of peer %pM from wmi list\n", ast_entry->addr, peer->addr); ++ list_del_init(&ast_entry->wmi_list); ++ } ++ } ++ list_del(&ast_entry->ase_list); + + /* WDS, MEC type AST entries need to be deleted on NSS */ + if (ast_entry->next_hop) +- ath11k_nss_del_wds_peer(ar, peer, ast_entry->addr); ++ ath11k_nss_del_wds_peer(ar, peer->addr, peer->peer_id, ++ ast_entry->addr); + +- cancel_work_sync(&ast_entry->wds_wmi_wk); + kfree(ast_entry); +- + ab->num_ast_entries--; + } + +@@ -681,6 +720,10 @@ void ath11k_peer_cleanup(struct ath11k * + + lockdep_assert_held(&ar->conf_mutex); + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ mutex_lock(&ab->base_ast_lock); ++#endif ++ + mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + list_for_each_entry_safe(peer, tmp_peer, &ab->peers, list) { +@@ -709,6 +752,9 @@ void ath11k_peer_cleanup(struct ath11k * + + spin_unlock_bh(&ab->base_lock); + mutex_unlock(&ab->tbl_mtx_lock); ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ mutex_unlock(&ab->base_ast_lock); ++#endif + } + + static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) +@@ -743,12 +789,18 @@ static int __ath11k_peer_delete(struct a + int ret; + struct ath11k_peer *peer; + struct ath11k_base *ab = ar->ab; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct ath11k_ast_entry *ast_entry, *tmp_ast; ++#endif + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->peer_delete_done); + ath11k_nss_peer_delete(ar->ab, vdev_id, addr); + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ mutex_lock(&ab->base_ast_lock); ++#endif + mutex_lock(&ab->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + +@@ -771,17 +823,35 @@ static int __ath11k_peer_delete(struct a + return -EINVAL; + } + +- /* Check if the found peer is what we want to remove. +- * While the sta is transitioning to another band we may +- * have 2 peer with the same addr assigned to different +- * vdev_id. Make sure we are deleting the correct peer. +- */ +- if (peer && peer->vdev_id == vdev_id) ++ if (peer) { ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ peer->delete_in_progress = true; ++ if (peer->self_ast_entry) { ++ ath11k_peer_del_ast(ar, peer->self_ast_entry); ++ peer->self_ast_entry = NULL; ++ } ++ ++ list_for_each_entry_safe(ast_entry, tmp_ast, ++ &peer->ast_entry_list, ase_list) ++ if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || ++ (ast_entry->type == ATH11K_AST_TYPE_MEC)) { ++ if (!list_empty(&ast_entry->wmi_list)) { ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ "%s deleting unprocessed ast entry %pM of peer %pM from wmi list\n", ++ __func__, ast_entry->addr, addr); ++ list_del_init(&ast_entry->wmi_list); ++ } ++ } ++#endif + ath11k_peer_rhash_delete(ab, peer); ++ } + + spin_unlock_bh(&ab->base_lock); + mutex_unlock(&ab->tbl_mtx_lock); + ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ mutex_unlock(&ab->base_ast_lock); ++#endif + + ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); + if (ret) { +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -42,9 +42,7 @@ enum ath11k_wds_wmi_action { + struct ath11k_ast_entry { + u16 ast_idx; + u8 addr[ETH_ALEN]; +- u8 next_node_mac[ETH_ALEN]; + enum ath11k_wds_wmi_action action; +- struct work_struct wds_wmi_wk; + struct ath11k_peer *peer; + struct ath11k *ar; + bool next_hop; +@@ -55,9 +53,9 @@ struct ath11k_ast_entry { + u16 ast_hash_value; + int ref_cnt; + enum ath11k_ast_entry_type type; +- bool delete_in_progress; + void *cookie; + struct list_head ase_list; ++ struct list_head wmi_list; + }; + + struct ath11k_peer { +@@ -97,6 +95,7 @@ struct ath11k_peer { + bool dp_setup_done; + struct ppdu_user_delayba ppdu_stats_delayba; + bool delayba_flag; ++ bool delete_in_progress; + }; + + void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id); diff --git a/package/kernel/mac80211/patches/nss/ath11k/335-0001-ath11k-optimize-tx-completions.patch b/package/kernel/mac80211/patches/nss/ath11k/335-0001-ath11k-optimize-tx-completions.patch new file mode 100644 index 00000000000000..60f3b246550641 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/335-0001-ath11k-optimize-tx-completions.patch @@ -0,0 +1,235 @@ +From 34b4e65248e7e1605448b06a006347354990bfba Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 11 Nov 2021 10:30:35 +0530 +Subject: [PATCH] ath11k: optimize tx completions + +Process the required fields from tx completion status +in case of stats disabled. + +Signed-off-by: Venkateswara Naralasetty +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 114 ++++++++++++++++---------------- + 1 file changed, 58 insertions(+), 56 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -651,9 +651,41 @@ err_out: + spin_unlock_bh(&ab->base_lock); + } + ++static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, ++ struct hal_wbm_release_ring *desc, ++ struct hal_tx_status *ts) ++{ ++ ts->buf_rel_source = ++ FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); ++ if (unlikely(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && ++ ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) ++ return; ++ ++ if (unlikely(ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) ++ return; ++ ++ ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, ++ desc->info0); ++ ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, ++ desc->info1); ++ ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, ++ desc->info1); ++ ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, ++ desc->info2); ++ if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) ++ ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; ++ ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); ++ ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); ++ if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID) ++ ts->rate_stats = desc->rate_stats.info0; ++ else ++ ts->rate_stats = 0; ++} ++ + static void ath11k_dp_tx_complete_msdu(struct ath11k *ar, + struct sk_buff *msdu, +- struct hal_tx_status *ts) ++ struct hal_wbm_release_ring *tx_status, ++ enum hal_wbm_rel_src_module buf_rel_source) + { + struct ieee80211_tx_status status = { 0 }; + struct ieee80211_rate_status status_rate = { 0 }; +@@ -663,9 +695,11 @@ static void ath11k_dp_tx_complete_msdu(s + struct ath11k_peer *peer; + struct ath11k_sta *arsta; + struct rate_info rate; ++ struct hal_tx_status ts = { 0 }; ++ enum hal_wbm_tqm_rel_reason rel_status; + u8 flags = 0; + +- if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { ++ if (unlikely(WARN_ON_ONCE(buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM))) { + /* Must not happen */ + return; + } +@@ -674,11 +708,14 @@ static void ath11k_dp_tx_complete_msdu(s + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); + +- /* Free skb here if stats is disabled */ ++ rel_status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, ++ tx_status->info0); ++ ++ /* Free skb here if stats is disabled */ + if (ab->stats_disable && !(flags & ATH11K_SKB_TX_STATUS)) { + if (msdu->destructor) { + msdu->wifi_acked_valid = 1; +- msdu->wifi_acked = ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED; ++ msdu->wifi_acked = rel_status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED; + } + if (skb_has_frag_list(msdu)) { + kfree_skb_list(skb_shinfo(msdu)->frag_list); +@@ -688,6 +725,8 @@ static void ath11k_dp_tx_complete_msdu(s + return; + } + ++ ath11k_dp_tx_status_parse(ab, tx_status, &ts); ++ + if (unlikely(!rcu_access_pointer(ab->pdevs_active[ar->pdev_idx]))) { + ieee80211_free_txskb(ar->hw, msdu); + return; +@@ -704,48 +743,48 @@ static void ath11k_dp_tx_complete_msdu(s + /* skip tx rate update from ieee80211_status*/ + info->status.rates[0].idx = -1; + +- if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && ++ if (ts.status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED && + !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR + +- ts->ack_rssi; ++ ts.ack_rssi; + info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID; + } + +- if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && ++ if (ts.status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX && + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)) || + ab->hw_params.single_pdev_only) { +- if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { ++ if (ts.flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) { + if (ar->last_ppdu_id == 0) { +- ar->last_ppdu_id = ts->ppdu_id; +- } else if (ar->last_ppdu_id == ts->ppdu_id || ++ ar->last_ppdu_id = ts.ppdu_id; ++ } else if (ar->last_ppdu_id == ts.ppdu_id || + ar->cached_ppdu_id == ar->last_ppdu_id) { + ar->cached_ppdu_id = ar->last_ppdu_id; + ar->cached_stats.is_ampdu = true; +- ath11k_dp_tx_update_txcompl(ar, ts); ++ ath11k_dp_tx_update_txcompl(ar, &ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } else { + ar->cached_stats.is_ampdu = false; +- ath11k_dp_tx_update_txcompl(ar, ts); ++ ath11k_dp_tx_update_txcompl(ar, &ts); + memset(&ar->cached_stats, 0, + sizeof(struct ath11k_per_peer_tx_stats)); + } +- ar->last_ppdu_id = ts->ppdu_id; ++ ar->last_ppdu_id = ts.ppdu_id; + } + +- ath11k_dp_tx_cache_peer_stats(ar, msdu, ts); ++ ath11k_dp_tx_cache_peer_stats(ar, msdu, &ts); + } + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ts->peer_id); ++ peer = ath11k_peer_find_by_id(ab, ts.peer_id); + if (unlikely(!peer || !peer->sta)) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "dp_tx: failed to find the peer with peer_id %d\n", +- ts->peer_id); ++ ts.peer_id); + spin_unlock_bh(&ab->base_lock); + ieee80211_free_txskb(ar->hw, msdu); + return; +@@ -767,37 +806,6 @@ static void ath11k_dp_tx_complete_msdu(s + ieee80211_tx_status_ext(ar->hw, &status); + } + +-static inline void ath11k_dp_tx_status_parse(struct ath11k_base *ab, +- struct hal_wbm_release_ring *desc, +- struct hal_tx_status *ts) +-{ +- ts->buf_rel_source = +- FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, desc->info0); +- if (unlikely(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && +- ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) +- return; +- +- if (unlikely(ts->buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) +- return; +- +- ts->status = FIELD_GET(HAL_WBM_RELEASE_INFO0_TQM_RELEASE_REASON, +- desc->info0); +- ts->ppdu_id = FIELD_GET(HAL_WBM_RELEASE_INFO1_TQM_STATUS_NUMBER, +- desc->info1); +- ts->try_cnt = FIELD_GET(HAL_WBM_RELEASE_INFO1_TRANSMIT_COUNT, +- desc->info1); +- ts->ack_rssi = FIELD_GET(HAL_WBM_RELEASE_INFO2_ACK_FRAME_RSSI, +- desc->info2); +- if (desc->info2 & HAL_WBM_RELEASE_INFO2_FIRST_MSDU) +- ts->flags |= HAL_TX_STATUS_FLAGS_FIRST_MSDU; +- ts->peer_id = FIELD_GET(HAL_WBM_RELEASE_INFO3_PEER_ID, desc->info3); +- ts->tid = FIELD_GET(HAL_WBM_RELEASE_INFO3_TID, desc->info3); +- if (desc->rate_stats.info0 & HAL_TX_RATE_STATS_INFO0_VALID) +- ts->rate_stats = desc->rate_stats.info0; +- else +- ts->rate_stats = 0; +-} +- + static inline bool ath11k_dp_tx_completion_valid(struct hal_wbm_release_ring *desc) + { + struct htt_tx_wbm_completion *status_desc; +@@ -821,9 +829,9 @@ void ath11k_dp_tx_completion_handler(str + int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id, count = 0, i = 0; + struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; + struct sk_buff *msdu; +- struct hal_tx_status ts = { 0 }; + struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; + int valid_entries; ++ enum hal_wbm_rel_src_module buf_rel_source; + u32 *desc; + u32 msdu_id, desc_id; + u8 mac_id; +@@ -863,14 +871,16 @@ void ath11k_dp_tx_completion_handler(str + + while (count--) { + tx_status = &tx_ring->tx_status[i++]; +- ath11k_dp_tx_status_parse(ab, tx_status, &ts); + + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + tx_status->buf_addr_info.info1); + mac_id = FIELD_GET(DP_TX_DESC_ID_MAC_ID, desc_id); + msdu_id = FIELD_GET(DP_TX_DESC_ID_MSDU_ID, desc_id); + +- if (unlikely(ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) { ++ buf_rel_source = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, ++ tx_status->info0); ++ ++ if (unlikely(buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW)) { + ath11k_dp_tx_process_htt_tx_complete(ab, + (void *)tx_status, + mac_id, msdu_id, +@@ -894,7 +904,7 @@ void ath11k_dp_tx_completion_handler(str + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + +- ath11k_dp_tx_complete_msdu(ar, msdu, &ts); ++ ath11k_dp_tx_complete_msdu(ar, msdu, tx_status, buf_rel_source); + } + } + diff --git a/package/kernel/mac80211/patches/nss/ath11k/335-0002-ath11k-use-DECLARE_BITMAP-for-idr-operations.patch b/package/kernel/mac80211/patches/nss/ath11k/335-0002-ath11k-use-DECLARE_BITMAP-for-idr-operations.patch new file mode 100644 index 00000000000000..f567ec640942c9 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/335-0002-ath11k-use-DECLARE_BITMAP-for-idr-operations.patch @@ -0,0 +1,286 @@ +From 6cd8cb301e431860e71b8d3453846a9ed0efea81 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 11 Nov 2021 10:38:26 +0530 +Subject: [PATCH] ath11k: use DECLARE_BITMAP for idr operations + +Use DECLARE_BITMAP for declaring bit map for idrs. And use APIs +find_first_zero_bit, set_bit and clear bit instead of idr_alloc +and idr_destroy. + +This helps in improving idle CPU and throughput. + +Signed-off-by: Venkateswara Naralasetty +--- + drivers/net/wireless/ath/ath11k/dp.c | 34 +++++++++++++----- + drivers/net/wireless/ath/ath11k/dp.h | 10 +++++- + drivers/net/wireless/ath/ath11k/dp_tx.c | 61 ++++++++++++++++++--------------- + 3 files changed, 69 insertions(+), 36 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -376,6 +376,8 @@ static void ath11k_dp_srng_common_cleanu + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring); + ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring); ++ kfree(dp->tx_ring[i].idr_pool); ++ dp->tx_ring[i].idr_pool = NULL; + } + ath11k_dp_srng_cleanup(ab, &dp->reo_reinject_ring); + ath11k_dp_srng_cleanup(ab, &dp->rx_rel_ring); +@@ -388,7 +390,7 @@ static int ath11k_dp_srng_common_setup(s + { + struct ath11k_dp *dp = &ab->dp; + struct hal_srng *srng; +- int i, ret; ++ int i, ret, j; + u8 tcl_num, wbm_num; + + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, +@@ -447,6 +449,18 @@ static int ath11k_dp_srng_common_setup(s + ath11k_dp_shadow_init_timer(ab, &dp->tx_ring_timer[i], + ATH11K_SHADOW_DP_TIMER_INTERVAL, + dp->tx_ring[i].tcl_data_ring.ring_id); ++ ++ dp->tx_ring[i].idr_pool = kcalloc(DP_TX_IDR_SIZE, ++ sizeof(struct idr_entry), GFP_KERNEL); ++ if (!dp->tx_ring[i].idr_pool) { ++ ath11k_warn(ab, "failed to allocate memory for idr pool ring(%d)\n", i); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* Reset id to default */ ++ for (j = 0; j < DP_TX_IDR_SIZE; j++) ++ dp->tx_ring[i].idr_pool[j].id = -1; + } + + ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT, +@@ -1031,9 +1045,8 @@ void ath11k_dp_vdev_tx_attach(struct ath + ath11k_dp_update_vdev_search(arvif); + } + +-static int ath11k_dp_tx_pending_cleanup(int buf_id, void *skb, void *ctx) ++static int ath11k_dp_tx_pending_cleanup(struct ath11k_base *ab, void *skb) + { +- struct ath11k_base *ab = ctx; + struct sk_buff *msdu = skb; + + dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len, +@@ -1047,21 +1060,28 @@ static int ath11k_dp_tx_pending_cleanup( + void ath11k_dp_free(struct ath11k_base *ab) + { + struct ath11k_dp *dp = &ab->dp; +- int i; ++ int i, j; + + ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks, + HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring); + ++ for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { ++ struct dp_tx_ring *tx_ring = &dp->tx_ring[i]; ++ spin_lock_bh(&tx_ring->tx_idr_lock); ++ for(j = 0; j < DP_TX_IDR_SIZE; j++) { ++ ++ if (test_and_clear_bit(j, tx_ring->idrs)) ++ ath11k_dp_tx_pending_cleanup(ab, tx_ring->idr_pool[j].buf); ++ } ++ ++ spin_unlock_bh(&tx_ring->tx_idr_lock); ++ } ++ + ath11k_dp_srng_common_cleanup(ab); + + ath11k_dp_reo_cmd_list_cleanup(ab); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) { +- spin_lock_bh(&dp->tx_ring[i].tx_idr_lock); +- idr_for_each(&dp->tx_ring[i].txbuf_idr, +- ath11k_dp_tx_pending_cleanup, ab); +- idr_destroy(&dp->tx_ring[i].txbuf_idr); +- spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock); + kfree(dp->tx_ring[i].tx_status); + } + +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -8,6 +8,7 @@ + #define ATH11K_DP_H + + #include "hal_rx.h" ++#include "hw.h" + + #define MAX_RXDMA_PER_PDEV 2 + +@@ -78,6 +79,13 @@ struct dp_rxdma_ring { + + #define ATH11K_TX_COMPL_NEXT(x) (((x) + 1) % DP_TX_COMP_RING_SIZE) + ++struct idr_entry { ++ int id; ++ void *buf; ++}; ++ ++#define DP_TX_IDR_SIZE ATH11K_DP_TX_COMP_RING_SIZE ++ + struct dp_tx_ring { + u8 tcl_data_ring_id; + struct dp_srng tcl_data_ring; +@@ -88,6 +96,8 @@ struct dp_tx_ring { + struct hal_wbm_release_ring *tx_status; + int tx_status_head; + int tx_status_tail; ++ DECLARE_BITMAP(idrs, DP_TX_IDR_SIZE); ++ struct idr_entry *idr_pool; + }; + + enum dp_mon_status_buf_state { +@@ -207,7 +217,6 @@ struct ath11k_pdev_dp { + #define DP_TCL_DATA_RING_SIZE 512 + #define DP_TCL_DATA_RING_SIZE_WCN6750 2048 + #define DP_TX_COMP_RING_SIZE ATH11K_DP_TX_COMP_RING_SIZE +-#define DP_TX_IDR_SIZE DP_TX_COMP_RING_SIZE + #define DP_TX_COMP_MAX_ALLOWED DP_TX_COMP_RING_SIZE + #define DP_TCL_CMD_RING_SIZE 32 + #define DP_TCL_STATUS_RING_SIZE 32 +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -135,6 +135,7 @@ int ath11k_dp_tx(struct ath11k *ar, stru + u8 ring_map = 0; + bool tcl_ring_retry, is_diff_encap = false; + u8 align_pad, htt_meta_size = 0, max_tx_ring, tcl_ring_id, ring_id; ++ u32 idr; + + if (unlikely(!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && + !ieee80211_is_data(hdr->frame_control))) +@@ -164,24 +165,28 @@ tcl_ring_sel: + tx_ring = &dp->tx_ring[tcl_ring_id]; + + spin_lock_bh(&tx_ring->tx_idr_lock); +- ret = idr_alloc(&tx_ring->txbuf_idr, skb, 0, +- DP_TX_IDR_SIZE - 1, GFP_ATOMIC); +- spin_unlock_bh(&tx_ring->tx_idr_lock); +- +- if (unlikely(ret < 0)) { ++ idr = find_first_zero_bit(tx_ring->idrs, DP_TX_IDR_SIZE); ++ if (unlikely(idr >= DP_TX_IDR_SIZE)) { + if (ring_map == (BIT(max_tx_ring) - 1) || + !ab->hw_params.tcl_ring_retry) { ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + ab->soc_stats.tx_err.idr_na[tcl_ring_id]++; + return -ENOSPC; + } + + /* Check if the next ring is available */ ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + ring_selector++; + goto tcl_ring_sel; + } + ++ set_bit(idr, tx_ring->idrs); ++ tx_ring->idr_pool[idr].id = idr; ++ tx_ring->idr_pool[idr].buf = skb; ++ spin_unlock_bh(&tx_ring->tx_idr_lock); ++ + ti.desc_id = FIELD_PREP(DP_TX_DESC_ID_MAC_ID, ar->pdev_idx) | +- FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, ret) | ++ FIELD_PREP(DP_TX_DESC_ID_MSDU_ID, idr) | + FIELD_PREP(DP_TX_DESC_ID_POOL_ID, pool_id); + ti.encap_type = ath11k_dp_tx_get_encap_type(arvif, skb); + +@@ -355,10 +360,9 @@ fail_unmap_dma: + fail_remove_idr: + if (ti.pkt_offset) + skb_pull(skb, ti.pkt_offset); +- spin_lock_bh(&tx_ring->tx_idr_lock); +- idr_remove(&tx_ring->txbuf_idr, +- FIELD_GET(DP_TX_DESC_ID_MSDU_ID, ti.desc_id)); +- spin_unlock_bh(&tx_ring->tx_idr_lock); ++ ++ tx_ring->idr_pool[idr].id = -1; ++ clear_bit(idr, tx_ring->idrs); + + if (tcl_ring_retry) + goto tcl_ring_sel; +@@ -367,16 +371,19 @@ fail_remove_idr: + } + + static void ath11k_dp_tx_free_txbuf(struct ath11k_base *ab, u8 mac_id, +- int msdu_id, ++ u32 msdu_id, + struct dp_tx_ring *tx_ring) + { + struct ath11k *ar; +- struct sk_buff *msdu; ++ struct sk_buff *msdu = NULL; + struct ath11k_skb_cb *skb_cb; + +- spin_lock(&tx_ring->tx_idr_lock); +- msdu = idr_remove(&tx_ring->txbuf_idr, msdu_id); +- spin_unlock(&tx_ring->tx_idr_lock); ++ if (msdu_id < DP_TX_IDR_SIZE && ++ tx_ring->idr_pool[msdu_id].id == msdu_id) { ++ msdu = tx_ring->idr_pool[msdu_id].buf; ++ tx_ring->idr_pool[msdu_id].id = -1; ++ clear_bit(msdu_id, tx_ring->idrs); ++ } + + if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", +@@ -400,16 +407,20 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + struct ath11k_dp_htt_wbm_tx_status *ts) + { + struct ieee80211_tx_status status = { 0 }; +- struct sk_buff *msdu; ++ struct sk_buff *msdu = NULL; + struct ieee80211_tx_info *info; + struct ath11k_skb_cb *skb_cb; + struct ath11k *ar; + struct ath11k_peer *peer; ++ u32 msdu_id = ts->msdu_id; + u8 flags = 0; + +- spin_lock(&tx_ring->tx_idr_lock); +- msdu = idr_remove(&tx_ring->txbuf_idr, ts->msdu_id); +- spin_unlock(&tx_ring->tx_idr_lock); ++ if (msdu_id < DP_TX_IDR_SIZE && ++ tx_ring->idr_pool[msdu_id].id == msdu_id) { ++ msdu = tx_ring->idr_pool[msdu_id].buf; ++ tx_ring->idr_pool[msdu_id].id = -1; ++ clear_bit(msdu_id, tx_ring->idrs); ++ } + + if (unlikely(!msdu)) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", +@@ -870,6 +881,7 @@ void ath11k_dp_tx_completion_handler(str + } + + while (count--) { ++ msdu=NULL; + tx_status = &tx_ring->tx_status[i++]; + + desc_id = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, +@@ -888,17 +900,19 @@ void ath11k_dp_tx_completion_handler(str + continue; + } + +- spin_lock(&tx_ring->tx_idr_lock); +- msdu = idr_remove(&tx_ring->txbuf_idr, msdu_id); ++ if (msdu_id < DP_TX_IDR_SIZE && ++ tx_ring->idr_pool[msdu_id].id == msdu_id) { ++ msdu = tx_ring->idr_pool[msdu_id].buf; ++ tx_ring->idr_pool[msdu_id].id = -1; ++ clear_bit(msdu_id, tx_ring->idrs); ++ } ++ + if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", + msdu_id); +- spin_unlock(&tx_ring->tx_idr_lock); + continue; + } + +- spin_unlock(&tx_ring->tx_idr_lock); +- + ar = ab->pdevs[mac_id].ar; + + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) diff --git a/package/kernel/mac80211/patches/nss/ath11k/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch b/package/kernel/mac80211/patches/nss/ath11k/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch new file mode 100644 index 00000000000000..8e018e619f12f8 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch @@ -0,0 +1,69 @@ +From 6a9662d48c4f277380283050370ab3f1f940b6a6 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Mon, 4 Sep 2023 13:44:47 +0530 +Subject: [PATCH] ath11k: skip HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE config + +Don't set HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE flag to TCL, +HW only take care of tid classification if this flag is not set. + +Signed-off-by: Venkateswara Naralasetty +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 19 +------------------ + drivers/net/wireless/ath/ath11k/hal_tx.c | 1 - + drivers/net/wireless/ath/ath11k/mac.c | 2 ++ + 3 files changed, 3 insertions(+), 19 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -44,19 +44,6 @@ static void ath11k_dp_tx_encap_nwifi(str + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); + } + +-static u8 ath11k_dp_tx_get_tid(struct sk_buff *skb) +-{ +- struct ieee80211_hdr *hdr = (void *)skb->data; +- struct ath11k_skb_cb *cb = ATH11K_SKB_CB(skb); +- +- if (cb->flags & ATH11K_SKB_HW_80211_ENCAP) +- return skb->priority & IEEE80211_QOS_CTL_TID_MASK; +- else if (!ieee80211_is_data_qos(hdr->frame_control)) +- return HAL_DESC_REO_NON_QOS_TID; +- else +- return skb->priority & IEEE80211_QOS_CTL_TID_MASK; +-} +- + enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher) + { + switch (cipher) { +@@ -232,10 +219,6 @@ tcl_ring_sel: + if (ieee80211_vif_is_mesh(arvif->vif)) + ti.enable_mesh = true; + +- ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE, 1); +- +- ti.tid = ath11k_dp_tx_get_tid(skb); +- + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: + if (arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) +--- a/drivers/net/wireless/ath/ath11k/hal_tx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_tx.c +@@ -65,7 +65,6 @@ void ath11k_hal_tx_cmd_desc_setup(struct + FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset); + + tcl_cmd.info2 = ti->flags1 | +- FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) | + FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id); + + tcl_cmd.info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX, +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -10150,6 +10150,8 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, USES_RSS); + } + ++ ieee80211_hw_set(ar->hw, SUPPORTS_TID_CLASS_OFFLOAD); ++ + ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; + ar->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + diff --git a/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-Fix-updating-rx-stats-with-monitor-vif-enable.patch b/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-Fix-updating-rx-stats-with-monitor-vif-enable.patch new file mode 100644 index 00000000000000..752218e9b8ec26 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-Fix-updating-rx-stats-with-monitor-vif-enable.patch @@ -0,0 +1,88 @@ +From 00d07d474f2ee3aa8aa2945fc26e473183e9201a Mon Sep 17 00:00:00 2001 +From: Anilkumar Kolli +Date: Fri, 17 Dec 2021 17:16:11 +0530 +Subject: [PATCH] ath11k: Fix updating rx stats with monitor vif enabled + +Rx stats update fails when monitor vif is enabled. +Current code does not update rx stats from monitor +status ring if monitor vif is enabled. +Add logic to update rx stats even if monitor vif enabled. + +Fixes: 1f49c59c7222 ("mac80211: Package upgrade") + +Signed-off-by: Anilkumar Kolli +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 96 ++++++---------------- + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -5953,12 +5953,23 @@ int ath11k_dp_rx_process_mon_status(stru + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + +- if (ppdu_info->peer_id == HAL_INVALID_PEERID || +- hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { ++ if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && ++ hal_status == HAL_RX_MON_STATUS_PPDU_DONE && ++ pmon->mon_ppdu_status == DP_PPDU_STATUS_START) { ++ rx_mon_stats->status_ppdu_done++; ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; ++ ++ if (!ab->hw_params.full_monitor_mode) { ++ ath11k_dp_rx_mon_dest_process(ar, mac_id, budget, napi); ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ++ } ++ } ++ ++ if ((ppdu_info->peer_id == HAL_INVALID_PEERID || ++ hal_status != HAL_RX_MON_STATUS_PPDU_DONE)) { + dev_kfree_skb_any(skb); + continue; + } +- + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); +@@ -6282,6 +6293,13 @@ static int ath11k_dp_full_mon_process_rx + + spin_lock_bh(&pmon->mon_lock); + ++ pmon->mon_ppdu_status = DP_PPDU_STATUS_START; ++ if (!test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) { ++ quota = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); ++ spin_unlock_bh(&pmon->mon_lock); ++ return quota; ++ } ++ + sw_mon_entries = &pmon->sw_mon_entries; + rx_mon_stats = &pmon->rx_mon_stats; + +@@ -6321,7 +6339,6 @@ static int ath11k_dp_full_mon_process_rx + } + + rx_mon_stats->dest_ppdu_done++; +- pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + pmon->buf_state = DP_MON_STATUS_LAG; + pmon->mon_status_paddr = sw_mon_entries->mon_status_paddr; + pmon->hold_mon_dst_ring = true; +@@ -6352,16 +6369,10 @@ reap_status_ring: + int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, + struct napi_struct *napi, int budget) + { +- struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); +- int ret = 0; +- +- if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && +- ab->hw_params.full_monitor_mode) +- ret = ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); +- else +- ret = ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); +- +- return ret; ++ if (ab->hw_params.full_monitor_mode) ++ return ath11k_dp_full_mon_process_rx(ab, mac_id, napi, budget); ++ else ++ return ath11k_dp_rx_process_mon_status(ab, mac_id, napi, budget); + } + + static int ath11k_dp_rx_pdev_mon_status_attach(struct ath11k *ar) diff --git a/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-skip-status-ring-entry-processing.patch b/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-skip-status-ring-entry-processing.patch new file mode 100644 index 00000000000000..0adf26c808aec6 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/336-ath11k-skip-status-ring-entry-processing.patch @@ -0,0 +1,191 @@ +From 0e79fe8c6937e080816230892d5382af5a5a86c6 Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Mon, 29 Nov 2021 11:07:53 +0530 +Subject: [PATCH] ath11k: skip status ring entry processing + +If STATUS_BUFFER_DONE is not set for a monitor status ring entry, +we don't process the status ring until STATUS_BUFFER_DONE set +for that status ring entry. + +During LMAC reset it may happnen that HW will not write +STATUS_BUFFER_DONE tlv in status buffer, in that case we end up +waiting for STATUS_BUFFER_DONE leading to backpressure on monitor +status ring. + +As per MAC team's suggestion, when HP + 1 entry is peeked and if DMA +is not done and if HP + 2 entry's DMA done is set, +replenish HP + 1 entry and start processing in next interrupt. +If HP + 2 entry's DMA done is not set, +poll onto HP + 1 entry DMA done to be set. + +Also, During monitor attach HP points to the end of the ring and TP +points to the start of the ring. Using ath11k_hal_srng_src_peek() +may result in processing invali buffer for the very first interrupt. +Since, HW starts writing buffer from TP. + +Hence, replaced ath11k_hal_srng_src_peek() with +ath11k_hal_srng_src_next_peek(). + +Signed-off-by: Venkateswara Naralasetty +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 92 ++++++++++++++++++++++++++++----- + drivers/net/wireless/ath/ath11k/hal.c | 30 +++++++++++ + drivers/net/wireless/ath/ath11k/hal.h | 4 ++ + 3 files changed, 113 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -3660,6 +3660,46 @@ ath11k_dp_rx_mon_update_status_buf_state + } + } + ++enum dp_mon_status_buf_state ++dp_rx_mon_handle_status_buf_done(struct ath11k_base *ab, struct hal_srng *srng, ++ struct dp_rxdma_ring *rx_ring) ++{ ++ void *status_desc; ++ struct sk_buff *skb; ++ struct ath11k_skb_rxcb *rxcb; ++ struct hal_tlv_hdr *tlv; ++ dma_addr_t paddr; ++ u32 cookie; ++ int buf_id; ++ u8 rbm; ++ ++ status_desc = ath11k_hal_srng_src_next_peek(ab, srng); ++ if (!status_desc) ++ return DP_MON_STATUS_NO_DMA; ++ ++ ath11k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm); ++ ++ buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); ++ ++ spin_lock_bh(&rx_ring->idr_lock); ++ skb = idr_find(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); ++ ++ if (!skb) ++ return DP_MON_STATUS_NO_DMA; ++ ++ rxcb = ATH11K_SKB_RXCB(skb); ++ dma_sync_single_for_cpu(ab->dev, rxcb->paddr, ++ skb->len + skb_tailroom(skb), ++ DMA_FROM_DEVICE); ++ ++ tlv = (struct hal_tlv_hdr *)skb->data; ++ if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_STATUS_BUFFER_DONE) ++ return DP_MON_STATUS_NO_DMA; ++ ++ return DP_MON_STATUS_REPLINISH; ++} ++ + static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) + { +@@ -3673,6 +3713,7 @@ static int ath11k_dp_rx_reap_mon_status_ + struct sk_buff *skb; + struct ath11k_skb_rxcb *rxcb; + struct hal_tlv_hdr *tlv; ++ enum dp_mon_status_buf_state reap_status; + u32 cookie; + int buf_id, srng_id; + dma_addr_t paddr; +@@ -3692,8 +3733,7 @@ static int ath11k_dp_rx_reap_mon_status_ + ath11k_hal_srng_access_begin(ab, srng); + while (*budget) { + *budget -= 1; +- rx_mon_status_desc = +- ath11k_hal_srng_src_peek(ab, srng); ++ rx_mon_status_desc = ath11k_hal_srng_src_peek(ab, srng); + if (!rx_mon_status_desc) { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + break; +@@ -3724,18 +3764,43 @@ static int ath11k_dp_rx_reap_mon_status_ + tlv = (struct hal_tlv_hdr *)skb->data; + if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != + HAL_RX_STATUS_BUFFER_DONE) { +- ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n", +- FIELD_GET(HAL_TLV_HDR_TAG, +- tlv->tl), buf_id); +- /* If done status is missing, hold onto status +- * ring until status is done for this status +- * ring buffer. +- * Keep HP in mon_status_ring unchanged, +- * and break from here. +- * Check status for same buffer for next time ++ /* RxDMA status done bit might not be set even ++ * though tp is moved by HW. + */ +- pmon->buf_state = DP_MON_STATUS_NO_DMA; +- break; ++ ++ /* If done status is missing: ++ * 1. As per MAC team's suggestion, ++ * when HP + 1 entry is peeked and if DMA ++ * is not done and if HP + 2 entry's DMA done ++ * is set. skip HP + 1 entry and ++ * start processing in next interrupt. ++ * 2. If HP + 2 entry's DMA done is not set, ++ * poll onto HP + 1 entry DMA done to be set. ++ * Check status for same buffer for next time ++ * dp_rx_mon_status_srng_process ++ */ ++ ++ reap_status = dp_rx_mon_handle_status_buf_done(ab, srng, ++ rx_ring); ++ if (reap_status == DP_MON_STATUS_NO_DMA) { ++ continue; ++ } else if (reap_status == DP_MON_STATUS_REPLINISH) { ++ ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n", ++ FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl), ++ buf_id); ++ ++ spin_lock_bh(&rx_ring->idr_lock); ++ idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); ++ ++ dma_unmap_single(ab->dev, rxcb->paddr, ++ skb->len + skb_tailroom(skb), ++ DMA_FROM_DEVICE); ++ ++ dev_kfree_skb_any(skb); ++ pmon->buf_state = DP_MON_STATUS_REPLINISH; ++ goto move_next; ++ } + } + + spin_lock_bh(&rx_ring->idr_lock); +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -837,6 +837,20 @@ u32 *ath11k_hal_srng_src_get_next_reaped + return desc; + } + ++u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, struct hal_srng *srng) ++{ ++ u32 next_hp; ++ ++ lockdep_assert_held(&srng->lock); ++ ++ next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; ++ ++ if (next_hp != srng->u.src_ring.cached_tp) ++ return srng->ring_base_vaddr + next_hp; ++ ++ return NULL; ++} ++ + u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng) + { + lockdep_assert_held(&srng->lock); +--- a/drivers/net/wireless/ath/ath11k/hal.h ++++ b/drivers/net/wireless/ath/ath11k/hal.h +@@ -952,6 +952,8 @@ int ath11k_hal_srng_dst_num_free(struct + void ath11k_hal_srng_dst_invalidate_entry(struct ath11k_base *ab, + struct hal_srng *srng, int entries); + u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng); ++u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, ++ struct hal_srng *srng); + u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab, + struct hal_srng *srng); + u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab, diff --git a/package/kernel/mac80211/patches/nss/ath11k/341-ath11k-fix-support-for-ext-vdev-in-NSS-for-AP_VLAN-v.patch b/package/kernel/mac80211/patches/nss/ath11k/341-ath11k-fix-support-for-ext-vdev-in-NSS-for-AP_VLAN-v.patch new file mode 100644 index 00000000000000..9a3c95a9df3c40 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/341-ath11k-fix-support-for-ext-vdev-in-NSS-for-AP_VLAN-v.patch @@ -0,0 +1,52 @@ +From 516c1d2faf4480c355ec5000d6d4ad4fc287d0e8 Mon Sep 17 00:00:00 2001 +From: Aditya Kumar Singh +Date: Thu, 30 Dec 2021 17:44:40 +0530 +Subject: [PATCH] ath11k: fix support for ext vdev in NSS for AP_VLAN vif + during recovery + +When driver is recovering, it tears down nss connection and sets +ab->nss.enabled as false. However, this is not set back to its original +state post recovery and hence when ext vdev is added again, warning +trace appears since ath11k_mac_op_add_interface() does not handle +NL80211_IFTYPE_AP_VLAN vif with nss disabled and throws a WARN_ON(1). + +[ 2777.201255] ------------[ cut here ]------------ +[ 2777.209669] WARNING: CPU: 0 PID: 77 at drivers/net/wireless/ath/ath11k/mac.c:7298 ath11k_mac_op_add_interface+0x314/0xa98 [ath11k] +[----- Trimmed -----] +[ 2777.499054] CPU: 0 PID: 77 Comm: kworker/0:2 Tainted: G W 5.4.89 #0 +[ 2777.515132] Hardware name: Generic DT based system +[ 2777.522658] Workqueue: events_freezable ieee80211_restart_work [mac80211] +[ 2777.527305] [<8030f3a4>] (unwind_backtrace) from [<8030b700>] (show_stack+0x10/0x14) +[ 2777.534153] [<8030b700>] (show_stack) from [<808da410>] (dump_stack+0x88/0xa8) +[ 2777.541964] [<808da410>] (dump_stack) from [<8031bf20>] (__warn+0xa4/0xd0) +[ 2777.548994] [<8031bf20>] (__warn) from [<8031bfbc>] (warn_slowpath_fmt+0x70/0x9c) +[ 2777.555972] [<8031bfbc>] (warn_slowpath_fmt) from [] (ath11k_mac_op_add_interface+0x314/0xa98 [ath11k]) +[ 2777.563664] [] (ath11k_mac_op_add_interface [ath11k]) from [<7fd34720>] (drv_add_interface+0x68/0x78 [mac80211]) +[ 2777.573928] [<7fd34720>] (drv_add_interface [mac80211]) from [<7fd679bc>] (ieee80211_reconfig+0x240/0xce8 [mac80211]) +[ 2777.584843] [<7fd679bc>] (ieee80211_reconfig [mac80211]) from [<7fd311ec>] (ieee80211_restart_work+0xd4/0xf4 [mac80211]) +[ 2777.595401] [<7fd311ec>] (ieee80211_restart_work [mac80211]) from [<80331968>] (process_one_work+0x1dc/0x310) +[ 2777.606205] [<80331968>] (process_one_work) from [<80332cf8>] (worker_thread+0x2bc/0x40c) +[ 2777.616009] [<80332cf8>] (worker_thread) from [<803373ac>] (kthread+0x164/0x180) +[ 2777.624167] [<803373ac>] (kthread) from [<803010e0>] (ret_from_fork+0x14/0x34) + +Added logic to enable nss as per MODULE_PARM_DESC parameter. + +Signed-off-by: Aditya Kumar Singh +--- + drivers/net/wireless/ath/ath11k/core.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1837,6 +1837,11 @@ static int ath11k_core_reconfigure_on_cr + + clear_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + ++ /* We have disabled NSS Offload support in the starting. ++ * Re-enabling it if it was originally enabled in the MODULE_PARM_DESC. ++ */ ++ ab->nss.enabled = nss_offload; ++ + ret = ath11k_core_qmi_firmware_ready(ab); + if (ret) + goto err_hal_srng_deinit; diff --git a/package/kernel/mac80211/patches/nss/ath11k/342-ath11k-provide-access-of-the-dma-buffer-back-to-dma-.patch b/package/kernel/mac80211/patches/nss/ath11k/342-ath11k-provide-access-of-the-dma-buffer-back-to-dma-.patch new file mode 100644 index 00000000000000..db779080b71f96 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/342-ath11k-provide-access-of-the-dma-buffer-back-to-dma-.patch @@ -0,0 +1,28 @@ +From a12157095a8a59eeaeb7c9efe70495288140159c Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Mon, 10 Jan 2022 12:38:10 +0530 +Subject: [PATCH] ath11k: provide access of the dma buffer back to dma device + +In ath11k_dbring_bufs_replenish, after accessing paddr which is a member of +ath11k_dbring_element, provide the access of the buffer back to +dma device by using dma_sync_single_for_device. + +Also the gfp flag to used inside ath11k_dbring_fill_bufs is changed +from GFP_KERNEL to GFP_ATOMIC as the buffers are allocated inside spin lock. + +Signed-off-by: Hari Chandrakanthan +--- + drivers/net/wireless/ath/ath11k/dbring.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dbring.c ++++ b/drivers/net/wireless/ath/ath11k/dbring.c +@@ -80,6 +80,8 @@ static int ath11k_dbring_bufs_replenish( + + buff->paddr = paddr; + ++ dma_sync_single_for_device(ab->dev, paddr, ring->buf_sz, DMA_FROM_DEVICE); ++ + cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) | + FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id); + diff --git a/package/kernel/mac80211/patches/nss/ath11k/353-ath11k-ignore-frags-from-uninitialized-peer-in-dp.patch b/package/kernel/mac80211/patches/nss/ath11k/353-ath11k-ignore-frags-from-uninitialized-peer-in-dp.patch new file mode 100644 index 00000000000000..0e99521c4d5ff0 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/353-ath11k-ignore-frags-from-uninitialized-peer-in-dp.patch @@ -0,0 +1,74 @@ +From 0ab84604de1db0f589f7303507f38148ba8f3f04 Mon Sep 17 00:00:00 2001 +From: Nagarajan Maran +Date: Tue, 28 Jun 2022 11:31:50 +0530 +Subject: [PATCH] ath11k: Ignore frags from uninitialized peer in dp + +In certain scenario, fragmented packet is received for self peer, +for which rx_tid and rx_frags are not initialized in datapath. +While handling this fragment, crash is observed as the +rx_frag list is uninitialised and when we walk in +ath11k_dp_rx_h_sort_frags, skb null leads to exception. + +To address this, before processing received fragments we +check dp_setup_done flag is set to ensure that peer +has completed its dp peer setup for fragment queue, +else ignore processing the fragments. + +Also, __fls would have an undefined behavior if the argument +is passed as "0". Hence, added changes to handle the same. + +Below is the call trace of the crash: + + CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.4.164 #0 + Hardware name: Qualcomm Technologies, Inc. IPQ6018/AP-CP01-C1 (DT) + pstate: a0400005 (NzCv daif +PAN -UAO) + pc : ath11k_dp_process_rx_err+0x550/0x1084 [ath11k] + lr : ath11k_dp_process_rx_err+0x4e0/0x1084 [ath11k] + + Call trace: + ath11k_dp_process_rx_err+0x550/0x1084 [ath11k] + ath11k_dp_service_srng+0x70/0x370 [ath11k] + 0xffffffc009693a04 + __napi_poll+0x30/0xa4 + net_rx_action+0x118/0x270 + __do_softirq+0x10c/0x244 + irq_exit+0x64/0xb4 + __handle_domain_irq+0x88/0xac + gic_handle_irq+0x74/0xbc + el1_irq+0xf0/0x1c0 + arch_cpu_idle+0x10/0x18 + do_idle+0x104/0x248 + cpu_startup_entry+0x20/0x64 + rest_init+0xd0/0xdc + arch_call_rest_init+0xc/0x14 + start_kernel+0x480/0x4b8 + Code: f9400281 f94066a2 91405021 b94a0023 (f9406401) + +Signed-off-by: Harshitha Prem +Signed-off-by: Nagarajan Maran +--- + drivers/net/wireless/ath/ath11k/dp.c | 3 ++- + drivers/net/wireless/ath/ath11k/dp_rx.c | 11 ++++++++++- + drivers/net/wireless/ath/ath11k/peer.h | 2 ++ + 3 files changed, 14 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -38,6 +38,7 @@ void ath11k_dp_peer_cleanup(struct ath11 + ath11k_peer_rx_tid_cleanup(ar, peer); + peer->dp_setup_done = false; + crypto_free_shash(peer->tfm_mmic); ++ peer->dp_setup_done = false; + spin_unlock_bh(&ab->base_lock); + } + +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -92,6 +92,7 @@ struct ath11k_peer { + u16 sec_type; + u16 sec_type_grp; + bool is_authorized; ++ /* Peer's datapath set flag */ + bool dp_setup_done; + struct ppdu_user_delayba ppdu_stats_delayba; + bool delayba_flag; diff --git a/package/kernel/mac80211/patches/nss/ath11k/356-ath11k-invalid-desc-sanity-check.patch b/package/kernel/mac80211/patches/nss/ath11k/356-ath11k-invalid-desc-sanity-check.patch new file mode 100644 index 00000000000000..f574597dceb969 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/356-ath11k-invalid-desc-sanity-check.patch @@ -0,0 +1,85 @@ +From c7e37aebbe19056eb7b61299e75c863263acf2b9 Mon Sep 17 00:00:00 2001 +From: Nagarajan Maran +Date: Tue, 9 Aug 2022 13:22:43 +0530 +Subject: [PATCH] Fix SKB corruption in REO destination ring + +In the traffic cases, randomly, invalid RX descriptor +from REO destination ring is received. This +invalid descriptor causes wrong SKB to be fetched +which in turn causes SKB memory corruption issue. + +Introduced Sanity check to validate the descriptor, +before processing the SKB. + +During the failure scenario, invalid RX descriptor +filled with values "0" is received which in-turn +corrupts the SKB stored in the ldr lookup +with buffer id "0". Changed the start id for +idr allocation to "1" and the buffer id "0" is +reserved for error validation. + + +Crash Signature : + +Unable to handle kernel paging request at virtual address 3f004900 +During the crash, +PC points to "b15_dma_inv_range+0x30/0x50" +LR points to "dma_cache_maint_page+0x8c/0x128". +The Backtrace obtained is as follows: +[<8031716c>] (b15_dma_inv_range) from [<80313a4c>] (dma_cache_maint_page+0x8c/0x128) +[<80313a4c>] (dma_cache_maint_page) from [<80313b90>] (__dma_page_dev_to_cpu+0x28/0xcc) +[<80313b90>] (__dma_page_dev_to_cpu) from [<7fb5dd68>] (ath11k_dp_process_rx+0x1e8/0x4a4 [ath11k]) +[<7fb5dd68>] (ath11k_dp_process_rx [ath11k]) from [<7fb53c20>] (ath11k_dp_service_srng+0xb0/0x2ac [ath11k]) +[<7fb53c20>] (ath11k_dp_service_srng [ath11k]) from [<7f67bba4>] (ath11k_pci_ext_grp_napi_poll+0x1c/0x78 [ath11k_pci]) +[<7f67bba4>] (ath11k_pci_ext_grp_napi_poll [ath11k_pci]) from [<807d5cf4>] (__napi_poll+0x28/0xb8) +[<807d5cf4>] (__napi_poll) from [<807d5f28>] (net_rx_action+0xf0/0x280) +[<807d5f28>] (net_rx_action) from [<80302148>] (__do_softirq+0xd0/0x280) +[<80302148>] (__do_softirq) from [<80320408>] (irq_exit+0x74/0xd4) +[<80320408>] (irq_exit) from [<803638a4>] (__handle_domain_irq+0x90/0xb4) +[<803638a4>] (__handle_domain_irq) from [<805bedec>] (gic_handle_irq+0x58/0x90) +[<805bedec>] (gic_handle_irq) from [<80301a78>] (__irq_svc+0x58/0x8c) + +Signed-off-by: Nagarajan Maran +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -396,8 +396,8 @@ int ath11k_dp_rxbufs_replenish(struct at + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); +- buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1, +- (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC); ++ buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1, ++ (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id <= 0) + goto fail_dma_unmap; +@@ -3141,6 +3141,16 @@ try_again: + while (likely(desc = + (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, + srng))) { ++ ++ push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, ++ desc->info0); ++ if (unlikely(push_reason == ++ HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED)) { ++ ath11k_warn(ab,"Received invalid desc\n"); ++ ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; ++ continue; ++ } ++ + cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, + desc->buf_addr_info.info1); + buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, +@@ -3171,8 +3181,6 @@ try_again: + + num_buffs_reaped[mac_id]++; + +- push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, +- desc->info0); + if (unlikely(push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) { + dev_kfree_skb_any(msdu); diff --git a/package/kernel/mac80211/patches/nss/ath11k/357-ath11k-fix-clear-peer-keys-during-disassoc.patch b/package/kernel/mac80211/patches/nss/ath11k/357-ath11k-fix-clear-peer-keys-during-disassoc.patch new file mode 100644 index 00000000000000..4c7fefbba155d7 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/357-ath11k-fix-clear-peer-keys-during-disassoc.patch @@ -0,0 +1,50 @@ +From 451cd8d4f107750d86a51c3cf750015676991d17 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Thu, 11 Aug 2022 21:27:16 +0530 +Subject: [PATCH] ath11k: fix clearing peer keys during sta state auth to assoc + +During station state change from AUTHORIZED to ASSOC, driver must clear its key +from hw. Ath11k clearing the keys during ASSOC to AUTH which is not a +valid sequence and there is a chance of accessing ptr after free. + +Clear the peer keys while the state of station changes from AUTHORIZED +to ASSOC. + +Signed-off-by: Karthikeyan Kathirvel +--- + drivers/net/wireless/ath/ath11k/mac.c | 37 ++++++++++++++++++++--------------- + 1 file changed, 21 insertions(+), 16 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4894,12 +4894,6 @@ static int ath11k_station_disassoc(struc + return ret; + } + +- ret = ath11k_clear_peer_keys(arvif, sta->addr); +- if (ret) { +- ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", +- arvif->vdev_id, ret); +- return ret; +- } + return 0; + } + +@@ -5490,6 +5484,17 @@ static int ath11k_mac_op_sta_state(struc + arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + arsta->bw_prev = arsta->bw; + spin_unlock_bh(&ar->data_lock); ++ ++ /* Driver should clear the peer keys during mac80211's ref ptr ++ * gets cleared in __sta_info_destroy_part2 (trans from ++ * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC) ++ */ ++ ret = ath11k_clear_peer_keys(arvif, sta->addr); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + spin_lock_bh(&ar->ab->base_lock); diff --git a/package/kernel/mac80211/patches/nss/ath11k/362-ath11k-fix-incorrect-ast-index-assignment-for-wds-peer.patch b/package/kernel/mac80211/patches/nss/ath11k/362-ath11k-fix-incorrect-ast-index-assignment-for-wds-peer.patch new file mode 100644 index 00000000000000..3786bd3de9647d --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/362-ath11k-fix-incorrect-ast-index-assignment-for-wds-peer.patch @@ -0,0 +1,87 @@ +From 396176575be9767a79d451fba4fe2931305f0435 Mon Sep 17 00:00:00 2001 +From: Raj Kumar Bhagat +Date: Mon, 7 Nov 2022 21:49:20 +0530 +Subject: [PATCH] ath11k: fix incorrect ast index assignment for wds peer + +Currently, same ast index is assigned for different nss wds peer mac +addresses which is incorrect. However, firmware provides the correct +and different ast index for different mac addresses. + +Hence, fix this issue by assigning the correct ast index provided by +the firmware. + +Signed-off-by: Raj Kumar Bhagat +--- + drivers/net/wireless/ath/ath11k/nss.c | 5 +++-- + drivers/net/wireless/ath/ath11k/nss.h | 6 ++++-- + drivers/net/wireless/ath/ath11k/peer.c | 3 +-- + 3 files changed, 8 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -3871,13 +3871,14 @@ msg_free: + } + + int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, +- u8 *dest_mac, enum ath11k_ast_entry_type type) ++ u8 *dest_mac, struct ath11k_ast_entry *ast_entry) + { + struct ath11k_base *ab = ar->ab; + struct nss_wifili_wds_peer_map_msg *wds_peer_map_msg; + struct nss_wifili_msg *wlmsg = NULL; + nss_wifili_msg_callback_t msg_cb; + nss_tx_status_t status; ++ enum ath11k_ast_entry_type type = ast_entry->type; + int ret = 0; + + wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); +@@ -3887,7 +3888,7 @@ int ath11k_nss_map_wds_peer(struct ath11 + wds_peer_map_msg = &wlmsg->msg.wdspeermapmsg; + + wds_peer_map_msg->vdev_id = peer->vdev_id; +- wds_peer_map_msg->ast_idx = peer->hw_peer_id; ++ wds_peer_map_msg->ast_idx = ast_entry->ast_idx; + + if (type == ATH11K_AST_TYPE_MEC) + wds_peer_map_msg->peer_id = NSS_WIFILI_MEC_PEER_ID; +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -20,6 +20,7 @@ struct ath11k; + struct ath11k_base; + struct ath11k_vif; + struct ath11k_peer; ++struct ath11k_ast_entry; + struct ath11k_sta; + enum ath11k_ast_entry_type; + struct hal_rx_mon_ppdu_info; +@@ -289,7 +290,7 @@ int ath11k_nss_add_wds_peer(struct ath11 + int ath11k_nss_update_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, + u8 *dest_mac); + int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, +- u8 *dest_mac, enum ath11k_ast_entry_type type); ++ u8 *dest_mac, struct ath11k_ast_entry *ast_entry); + int ath11k_nss_del_wds_peer(struct ath11k *ar, u8 *peer_addr, + int peer_id, u8 *dest_mac); + int ath11k_nss_ext_vdev_cfg_wds_peer(struct ath11k_vif *arvif, +@@ -408,7 +409,8 @@ static inline int ath11k_nss_update_wds_ + } + + static inline int ath11k_nss_map_wds_peer(struct ath11k *ar, struct ath11k_peer *peer, +- u8 *dest_mac, int type) ++ u8 *dest_mac, ++ struct ath11k_ast_entry *ast_entry) + { + return 0; + } +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -373,8 +373,7 @@ void ath11k_peer_map_ast(struct ath11k * + + if ((ast_entry->type == ATH11K_AST_TYPE_WDS) || + (ast_entry->type == ATH11K_AST_TYPE_MEC)) +- ath11k_nss_map_wds_peer(ar, peer, mac_addr, +- ast_entry->type); ++ ath11k_nss_map_wds_peer(ar, peer, mac_addr, ast_entry); + + ath11k_dbg(ab, ATH11K_DBG_MAC, "ath11k_peer_map_ast peer %pM ast_entry %pM\n", + peer->addr, ast_entry->addr); diff --git a/package/kernel/mac80211/patches/nss/ath11k/371-ath11k-Fix-ppdu_id-from-firmware-PPDU-stats.patch b/package/kernel/mac80211/patches/nss/ath11k/371-ath11k-Fix-ppdu_id-from-firmware-PPDU-stats.patch new file mode 100644 index 00000000000000..f909ba4a673f69 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/371-ath11k-Fix-ppdu_id-from-firmware-PPDU-stats.patch @@ -0,0 +1,52 @@ +From 537a8f2292ba9052957e8284161bcee0635e4223 Mon Sep 17 00:00:00 2001 +From: Ramya Gnanasekar +Date: Tue, 11 Apr 2023 14:15:36 +0530 +Subject: [PATCH] ath11k: Fix ppdu_id from firmware PPDU stats + +ppdu_id in USR_COMPLTN_ACK_BA_STATUS TLV will have firmware meta data +last 7 bits. +When ppdu_id is taken as such, during parsing this is treated as different +ppdu_id which causes incorrect stats update. Since firmware will use +this MSB 7 bits for internal accounting and optimization, +it is recommended to use first 25 bits when fetching ppdu for USR_COMPLTN_ACK_BA_STATUS. + +Signed-off-by: Ramya Gnanasekar + +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -1425,6 +1425,7 @@ struct htt_ppdu_stats_usr_cmpltn_cmn { + #define HTT_PPDU_STATS_ACK_BA_INFO_TID_NUM GENMASK(31, 25) + + #define HTT_PPDU_STATS_NON_QOS_TID 16 ++#define HTT_PPDU_STATS_PPDU_ID GENMASK(24, 0) + + struct htt_ppdu_stats_usr_cmpltn_ack_ba_status { + u32 ppdu_id; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1288,7 +1288,7 @@ static int ath11k_htt_tlv_ppdu_stats_par + struct htt_ppdu_user_stats *user_stats = NULL; + int cur_user; + u16 peer_id; +- u32 frame_type; ++ u32 frame_type, ppdu_id; + + ppdu_info = data; + +@@ -1369,6 +1369,8 @@ static int ath11k_htt_tlv_ppdu_stats_par + return -EINVAL; + } + ++ ppdu_id = ++ ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->ppdu_id; + peer_id = + ((struct htt_ppdu_stats_usr_cmpltn_ack_ba_status *)ptr)->sw_peer_id; + cur_user = ath11k_get_ppdu_user_index(&ppdu_info->ppdu_stats, +@@ -1380,6 +1382,7 @@ static int ath11k_htt_tlv_ppdu_stats_par + user_stats->is_valid_peer_id = true; + memcpy((void *)&user_stats->ack_ba, ptr, + sizeof(struct htt_ppdu_stats_usr_cmpltn_ack_ba_status)); ++ ppdu_info->ppdu_id = FIELD_GET(HTT_PPDU_STATS_PPDU_ID, ppdu_id); + user_stats->tlv_flags |= BIT(tag); + break; + case HTT_PPDU_STATS_TAG_USR_COMMON: diff --git a/package/kernel/mac80211/patches/nss/ath11k/373-ath11k-Add-retry-mechanism-for-update_rx_qu.patch b/package/kernel/mac80211/patches/nss/ath11k/373-ath11k-Add-retry-mechanism-for-update_rx_qu.patch new file mode 100644 index 00000000000000..8adb5448a1564c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/373-ath11k-Add-retry-mechanism-for-update_rx_qu.patch @@ -0,0 +1,384 @@ +From 17d1895bad45ec2ef4c8c430ac0fbaff2ff1cf39 Mon Sep 17 00:00:00 2001 +From: Manish Dharanenthiran +Date: Tue, 18 Apr 2023 15:01:42 +0530 +Subject: [PATCH 2/3] CHROMIUM: ath11k: Add retry mechanism for update_rx_queue + reo cmd + +While reo_cmd ring is full, peer delete update rx queue +will be failed as there is no space to send the command +in ring. During the failure scenario, host will do +dma_unmap and free the allocated address but the HW +still have that particular vaddr. So, on next alloc +cycle kernel will allocated the freed addr but HW will +still have the address. This will result in memory +corruption as the host will try to access/write that +memory which is already in-use. + +To avoid this corruption, added new retry mechanism +for HAL_REO_CMD_UPDATE_RX_QUEUE by adding new list to +dp struct and protecting with new lock for access. +This avoids the host free in failure case and will +be freed only when HW freed that particular vaddr. + +Also, updated below changes +1) reo_flush command for sending 1K desc in one +command instead of sending 11 command for single +TID. + +2) Set FWD_MPDU and valid bit flag so that reo +flush will happen soon instead of waiting to +flush the queue. + +Signed-off-by: Manish Dharanenthiran +Signed-off-by: Tamizh Chelvam Raja +--- + drivers/net/wireless/ath/ath11k/core.h | 3 + + drivers/net/wireless/ath/ath11k/debugfs.c | 12 ++ + drivers/net/wireless/ath/ath11k/dp.c | 2 + + drivers/net/wireless/ath/ath11k/dp.h | 15 ++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 167 +++++++++++++++++----- + 5 files changed, 161 insertions(+), 38 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -902,6 +902,9 @@ struct ath11k_soc_dp_stats { + u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; + u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; + u32 hal_reo_error[DP_REO_DST_RING_MAX]; ++ u32 hal_reo_cmd_drain; ++ u32 reo_cmd_cache_error; ++ u32 reo_cmd_update_rx_queue_error; + struct ath11k_soc_dp_tx_err_stats tx_err; + struct ath11k_dp_ring_bp_stats bp_stats; + }; +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -848,6 +848,18 @@ static ssize_t ath11k_debugfs_dump_soc_d + "\nNSS Transmit Failures: %d\n", + atomic_read(&soc_stats->tx_err.nss_tx_fail)); + ++ len += scnprintf(buf + len, size - len, ++ "\nHAL_REO_CMD_DRAIN Counter: %u\n", ++ soc_stats->hal_reo_cmd_drain); ++ ++ len += scnprintf(buf + len, size - len, ++ "\nREO_CMD_CACHE_FLUSH Failure: %u\n", ++ soc_stats->reo_cmd_cache_error); ++ ++ len += scnprintf(buf + len, size - len, ++ "\nREO_CMD_UPDATE_RX_QUEUE Failure: %u\n", ++ soc_stats->reo_cmd_update_rx_queue_error); ++ + len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len); + + if (len > size) +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -1102,8 +1102,10 @@ int ath11k_dp_alloc(struct ath11k_base * + + INIT_LIST_HEAD(&dp->reo_cmd_list); + INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list); ++ INIT_LIST_HEAD(&dp->reo_cmd_update_rx_queue_list); + INIT_LIST_HEAD(&dp->dp_full_mon_mpdu_list); + spin_lock_init(&dp->reo_cmd_lock); ++ spin_lock_init(&dp->reo_cmd_update_queue_lock); + + dp->reo_cmd_cache_flush_count = 0; + +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -24,6 +24,7 @@ struct dp_rx_tid { + u32 *vaddr; + dma_addr_t paddr; + u32 size; ++ u32 pending_desc_size; + u32 ba_win_sz; + bool active; + +@@ -51,6 +52,14 @@ struct dp_reo_cache_flush_elem { + unsigned long ts; + }; + ++struct dp_reo_update_rx_queue_elem { ++ struct list_head list; ++ struct dp_rx_tid data; ++ int peer_id; ++ u8 tid; ++ bool reo_cmd_update_rx_queue_resend_flag; ++}; ++ + struct dp_reo_cmd { + struct list_head list; + struct dp_rx_tid data; +@@ -295,6 +304,12 @@ struct ath11k_dp { + * - reo_cmd_cache_flush_count + */ + spinlock_t reo_cmd_lock; ++ struct list_head reo_cmd_update_rx_queue_list; ++ /** ++ * protects access to below field, ++ * - reo_cmd_update_rx_queue_list ++ */ ++ spinlock_t reo_cmd_update_queue_lock; + struct ath11k_hp_update_timer reo_cmd_timer; + struct ath11k_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX]; + }; +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -21,6 +21,9 @@ + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + ++static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, ++ enum hal_reo_cmd_status status); ++ + static inline + u8 *ath11k_dp_rx_h_80211_hdr(struct ath11k_base *ab, struct hal_rx_desc *desc) + { +@@ -672,13 +675,50 @@ static int ath11k_dp_rx_pdev_srng_alloc( + return 0; + } + ++static int ath11k_peer_rx_tid_delete_handler(struct ath11k_base *ab, ++ struct dp_rx_tid *rx_tid, u8 tid) ++{ ++ struct ath11k_hal_reo_cmd cmd = {0}; ++ struct ath11k_dp *dp = &ab->dp; ++ ++ lockdep_assert_held(&dp->reo_cmd_update_queue_lock); ++ ++ rx_tid->active = false; ++ cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; ++ cmd.addr_lo = lower_32_bits(rx_tid->paddr); ++ cmd.addr_hi = upper_32_bits(rx_tid->paddr); ++ cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; ++ ++ return ath11k_dp_tx_send_reo_cmd(ab, rx_tid, ++ HAL_REO_CMD_UPDATE_RX_QUEUE, ++ &cmd, ++ ath11k_dp_rx_tid_del_func); ++} ++ + void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) + { + struct ath11k_dp *dp = &ab->dp; + struct dp_reo_cmd *cmd, *tmp; + struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; ++ struct dp_reo_update_rx_queue_elem *cmd_queue, *tmp_queue; + struct dp_rx_tid *rx_tid; + ++ spin_lock_bh(&dp->reo_cmd_update_queue_lock); ++ list_for_each_entry_safe(cmd_queue, tmp_queue, ++ &dp->reo_cmd_update_rx_queue_list, ++ list) { ++ list_del(&cmd_queue->list); ++ rx_tid = &cmd_queue->data; ++ if (rx_tid->vaddr) { ++ dma_unmap_single(ab->dev, rx_tid->paddr, ++ rx_tid->size, DMA_BIDIRECTIONAL); ++ kfree(rx_tid->vaddr); ++ rx_tid->vaddr = NULL; ++ } ++ kfree(cmd_queue); ++ } ++ spin_unlock_bh(&dp->reo_cmd_update_queue_lock); ++ + spin_lock_bh(&dp->reo_cmd_lock); + list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { + list_del(&cmd->list); +@@ -724,14 +764,18 @@ static void ath11k_dp_reo_cmd_free(struc + } + } + +-static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, ++static int ath11k_dp_reo_cache_flush(struct ath11k_base *ab, + struct dp_rx_tid *rx_tid) + { + struct ath11k_hal_reo_cmd cmd = {0}; + unsigned long tot_desc_sz, desc_sz; + int ret; + +- tot_desc_sz = rx_tid->size; ++ if (rx_tid->pending_desc_size) ++ tot_desc_sz = rx_tid->pending_desc_size; ++ else ++ tot_desc_sz = rx_tid->size; ++ + desc_sz = ath11k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); + + while (tot_desc_sz > desc_sz) { +@@ -742,11 +786,17 @@ static void ath11k_dp_reo_cache_flush(st + HAL_REO_CMD_FLUSH_CACHE, &cmd, + NULL); + if (ret) +- ath11k_warn(ab, +- "failed to send HAL_REO_CMD_FLUSH_CACHE, tid %d (%d)\n", +- rx_tid->tid, ret); ++ rx_tid->pending_desc_size = tot_desc_sz + desc_sz; ++ ++ /* If this fails with ring full condition, then ++ * no need to retry below as it is expected to ++ * fail within short time ++ */ ++ if (ret == -ENOBUFS) ++ goto exit; + } + ++ rx_tid->pending_desc_size = desc_sz; + memset(&cmd, 0, sizeof(cmd)); + cmd.addr_lo = lower_32_bits(rx_tid->paddr); + cmd.addr_hi = upper_32_bits(rx_tid->paddr); +@@ -754,24 +804,21 @@ static void ath11k_dp_reo_cache_flush(st + ret = ath11k_dp_tx_send_reo_cmd(ab, rx_tid, + HAL_REO_CMD_FLUSH_CACHE, + &cmd, ath11k_dp_reo_cmd_free); +- if (ret) { +- ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", +- rx_tid->tid, ret); +- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, +- DMA_BIDIRECTIONAL); +- kfree(rx_tid->vaddr); +- rx_tid->vaddr = NULL; +- } ++ ++exit: ++ return ret; + } + + static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, + enum hal_reo_cmd_status status) + { + struct ath11k_base *ab = dp->ab; +- struct dp_rx_tid *rx_tid = ctx; ++ struct dp_rx_tid *rx_tid = ctx, *update_rx_tid; + struct dp_reo_cache_flush_elem *elem, *tmp; ++ struct dp_reo_update_rx_queue_elem *qelem, *qtmp; + + if (status == HAL_REO_CMD_DRAIN) { ++ ab->soc_stats.hal_reo_cmd_drain++; + goto free_desc; + } else if (status != HAL_REO_CMD_SUCCESS) { + /* Shouldn't happen! Cleanup in case of other failure? */ +@@ -780,6 +827,29 @@ static void ath11k_dp_rx_tid_del_func(st + return; + } + ++ /* Check if there is any pending rx_queue, if yes then update it */ ++ spin_lock_bh(&dp->reo_cmd_update_queue_lock); ++ list_for_each_entry_safe(qelem, qtmp, &dp->reo_cmd_update_rx_queue_list, ++ list) { ++ if (qelem->reo_cmd_update_rx_queue_resend_flag && ++ qelem->data.active) { ++ update_rx_tid = &qelem->data; ++ ++ if (ath11k_peer_rx_tid_delete_handler(ab, update_rx_tid, qelem->tid)) { ++ update_rx_tid->active = true; ++ break; ++ } ++ update_rx_tid->vaddr = NULL; ++ update_rx_tid->paddr = 0; ++ update_rx_tid->size = 0; ++ update_rx_tid->pending_desc_size = 0; ++ ++ list_del(&qelem->list); ++ kfree(qelem); ++ } ++ } ++ spin_unlock_bh(&dp->reo_cmd_update_queue_lock); ++ + elem = kzalloc(sizeof(*elem), GFP_ATOMIC); + if (!elem) + goto free_desc; +@@ -797,13 +867,20 @@ static void ath11k_dp_rx_tid_del_func(st + if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD || + time_after(jiffies, elem->ts + + msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) { ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ if (ath11k_dp_reo_cache_flush(ab, &elem->data)) { ++ ab->soc_stats.reo_cmd_cache_error++; ++ /* In failure case, just update the timestamp ++ * for flush cache elem and continue ++ */ ++ spin_lock_bh(&dp->reo_cmd_lock); ++ elem->ts = jiffies; ++ break; ++ } ++ spin_lock_bh(&dp->reo_cmd_lock); + list_del(&elem->list); + dp->reo_cmd_cache_flush_count--; +- spin_unlock_bh(&dp->reo_cmd_lock); +- +- ath11k_dp_reo_cache_flush(ab, &elem->data); + kfree(elem); +- spin_lock_bh(&dp->reo_cmd_lock); + } + } + spin_unlock_bh(&dp->reo_cmd_lock); +@@ -819,34 +896,48 @@ free_desc: + void ath11k_peer_rx_tid_delete(struct ath11k *ar, + struct ath11k_peer *peer, u8 tid) + { +- struct ath11k_hal_reo_cmd cmd = {0}; + struct dp_rx_tid *rx_tid = &peer->rx_tid[tid]; +- int ret; ++ struct dp_reo_update_rx_queue_elem *elem, *tmp; ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_dp *dp = &ab->dp; + + if (!rx_tid->active) + return; + +- rx_tid->active = false; ++ elem = kzalloc(sizeof(*elem), GFP_ATOMIC); ++ if (!elem) { ++ ath11k_warn(ar->ab, "failed to alloc reo_update_rx_queue_elem, rx tid %d\n", ++ rx_tid->tid); ++ return; ++ } ++ elem->reo_cmd_update_rx_queue_resend_flag = false; ++ elem->peer_id = peer->peer_id; ++ elem->tid = tid; ++ memcpy(&elem->data, rx_tid, sizeof(*rx_tid)); ++ spin_lock_bh(&dp->reo_cmd_update_queue_lock); ++ list_add_tail(&elem->list, &dp->reo_cmd_update_rx_queue_list); + +- cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; +- cmd.addr_lo = lower_32_bits(rx_tid->paddr); +- cmd.addr_hi = upper_32_bits(rx_tid->paddr); +- cmd.upd0 |= HAL_REO_CMD_UPD0_VLD; +- ret = ath11k_dp_tx_send_reo_cmd(ar->ab, rx_tid, +- HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, +- ath11k_dp_rx_tid_del_func); +- if (ret) { +- if (ret != -ESHUTDOWN) +- ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", +- tid, ret); +- dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, +- DMA_BIDIRECTIONAL); +- kfree(rx_tid->vaddr); ++ list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_update_rx_queue_list, ++ list) { ++ rx_tid = &elem->data; ++ ++ if (ath11k_peer_rx_tid_delete_handler(ab, rx_tid, elem->tid)) { ++ rx_tid->active = true; ++ ab->soc_stats.reo_cmd_update_rx_queue_error++; ++ elem->reo_cmd_update_rx_queue_resend_flag = true; ++ break; ++ } + rx_tid->vaddr = NULL; ++ rx_tid->paddr = 0; ++ rx_tid->size = 0; ++ rx_tid->pending_desc_size = 0; ++ ++ list_del(&elem->list); ++ kfree(elem); + } ++ spin_unlock_bh(&dp->reo_cmd_update_queue_lock); + +- rx_tid->paddr = 0; +- rx_tid->size = 0; ++ return; + } + + static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, diff --git a/package/kernel/mac80211/patches/nss/ath11k/376-ath11k-Add-nss-event-handler-support-for-link-desc.patch b/package/kernel/mac80211/patches/nss/ath11k/376-ath11k-Add-nss-event-handler-support-for-link-desc.patch new file mode 100644 index 00000000000000..df9fb62bcd17b4 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/376-ath11k-Add-nss-event-handler-support-for-link-desc.patch @@ -0,0 +1,73 @@ +From 69356d5f6947c8a6182e1c6283478ad40c6df37b Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam Raja +Date: Wed, 21 Jun 2023 20:26:02 +0530 +Subject: [PATCH] ath11k: Add nss event handler support for link desc + +Add NSS event handler support for NSS_WIFILI_LINK_DESC_INFO_MSG. +This event will be given to host from NSS for releasing +link descriptor which used for fragmentation. This needs to be +handled to release the link descriptor back to hardware for +other usage. + +Signed-off-by: Tamizh Chelvam Raja +--- + drivers/net/wireless/ath/ath11k/nss.c | 43 +++++++++++++++++++++++++++ + 1 file changed, 43 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -101,6 +101,43 @@ static void ath11k_nss_wifili_stats_sync + spin_unlock_bh(&ab->base_lock); + } + ++static void ath11k_nss_wifili_link_desc_set(struct ath11k_base *ab, void *desc, ++ struct ath11k_buffer_addr *buf_addr_info, ++ enum hal_wbm_rel_bm_act action) ++{ ++ struct hal_wbm_release_ring *dst_desc = desc; ++ ++ dst_desc->buf_addr_info = *buf_addr_info; ++ dst_desc->info0 |= FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, ++ HAL_WBM_REL_SRC_MODULE_SW) | ++ FIELD_PREP(HAL_WBM_RELEASE_INFO0_BM_ACTION, action) | ++ FIELD_PREP(HAL_WBM_RELEASE_INFO0_DESC_TYPE, ++ HAL_WBM_REL_DESC_TYPE_MSDU_LINK); ++} ++ ++static void ath11k_nss_wifili_link_desc_return(struct ath11k_base *ab, ++ struct ath11k_buffer_addr *buf_addr_info) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ struct hal_srng *srng; ++ u32 *desc; ++ ++ srng = &ab->hal.srng_list[dp->wbm_desc_rel_ring.ring_id]; ++ spin_lock_bh(&srng->lock); ++ ++ ath11k_hal_srng_access_begin(ab, srng); ++ desc = ath11k_hal_srng_src_get_next_entry(ab, srng); ++ ++ if (!desc) ++ goto exit; ++ ++ ath11k_nss_wifili_link_desc_set(ab, desc, buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); ++ ++exit: ++ ath11k_hal_srng_access_end(ab, srng); ++ spin_unlock(&srng->lock); ++} ++ + static void ath11k_nss_get_peer_stats(struct ath11k_base *ab, struct nss_wifili_peer_stats *stats) + { + struct ath11k_peer *peer; +@@ -369,6 +406,10 @@ void ath11k_nss_wifili_event_receive(str + ath11k_dbg(ab, ATH11K_DBG_NSS_MESH, "nss wifili mesh capability response %d\n", + ab->nss.mesh_nss_offload_enabled); + break; ++ case NSS_WIFILI_LINK_DESC_INFO_MSG: ++ ath11k_nss_wifili_link_desc_return(ab, ++ (void *)&msg->msg.linkdescinfomsg); ++ break; + default: + ath11k_dbg(ab, ATH11K_DBG_NSS, "unhandled event %d\n", msg_type); + break; diff --git a/package/kernel/mac80211/patches/nss/ath11k/401-ath11k-Fix-mutex-dead-lock-and-q6-dump-crash.patch b/package/kernel/mac80211/patches/nss/ath11k/401-ath11k-Fix-mutex-dead-lock-and-q6-dump-crash.patch new file mode 100644 index 00000000000000..ceeb15c8cbea65 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/401-ath11k-Fix-mutex-dead-lock-and-q6-dump-crash.patch @@ -0,0 +1,59 @@ +From 0a30d8c3d51d798b50bd792aa49e6a5b5ad14183 Mon Sep 17 00:00:00 2001 +From: Rajat Soni +Date: Wed, 12 Apr 2023 18:06:54 +0530 +Subject: [PATCH] ath11k: Fix mutex dead lock and q6 dump crash + +Issue 1: +Currently for HOST_DDR_REGION_TYPE memory, we are facing crash +because target memory virtual address (vaddr) is NULL. We are +not assigning any value to vaddr. + +Issue 2: +In ath11k_mac_op_start we are using mutex lock and waiting for +waiting for completion of ab->reconfigure_complete. +Before completing ab->reconfigure_complete in function +ath11k_core_reconfigure_on_crash, we are again trying +to get mutex lock in ath11k_spectral_deinit. This results +in dead lock. + +Due to these two issue during SSR case fw recovery pdev +is not recovered properly. + +To resolve these two issues: +Issue1: +During ath11k_qmi_assign_target_mem_chunk we should assign +ab->qmi.target_mem[idx].vaddr. + +Issue 2: +Unlock mutex lock before waiting for completion of +ab->reconfigure_complete and acquiring lock again. + +Signed-off-by: Rajat Soni +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 ++ + drivers/net/wireless/ath/ath11k/qmi.c | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6786,7 +6786,9 @@ static int ath11k_mac_op_start(struct ie + break; + case ATH11K_STATE_RESTARTING: + ar->state = ATH11K_STATE_RESTARTED; ++ mutex_unlock(&ar->conf_mutex); + ath11k_mac_wait_reconfigure(ab); ++ mutex_lock(&ar->conf_mutex); + break; + case ATH11K_STATE_RESTARTED: + case ATH11K_STATE_WEDGED: +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2061,6 +2061,8 @@ static int ath11k_qmi_assign_target_mem_ + if (!ab->qmi.target_mem[idx].iaddr) + return -EIO; + ++ ab->qmi.target_mem[idx].vaddr = ab->qmi.target_mem[idx].iaddr; ++ + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + host_ddr_sz = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; diff --git a/package/kernel/mac80211/patches/nss/ath11k/453-ath11k-flush-management-frames-to-firmware-before-wa.patch b/package/kernel/mac80211/patches/nss/ath11k/453-ath11k-flush-management-frames-to-firmware-before-wa.patch new file mode 100644 index 00000000000000..90982924fe29f7 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/453-ath11k-flush-management-frames-to-firmware-before-wa.patch @@ -0,0 +1,38 @@ +From 76482cd32e1053ef6437015d9418636616931213 Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Thu, 22 Jun 2023 00:45:47 +0530 +Subject: [PATCH] ath11k : flush management frames to firmware before waiting + for tx completion + +warning print "ath11k c000000.wifi: failed to flush mgmt transmit queue 0" +is observed during interface down. + +The management packets are queued in a skb_queue and the skb_queue +is dequeued in the work ar->wmi_mgmt_tx_work. + +In ath11k_mac_flush_tx_complete, before waiting for the tx completion of +all the management frames, we are not ensuring that queued +management frames are flushed to the firmware. + +This causes ar->num_pending_mgmt_tx to be positive and it leads to the +warning print. + +Fix this by flushing all the management frames to firmware before waiting +for the tx completion. + +Signed-off-by: Hari Chandrakanthan +--- + drivers/net/wireless/ath/ath11k/mac.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8464,6 +8464,8 @@ static int ath11k_mac_flush_tx_complete( + ret = -ETIMEDOUT; + } + ++ flush_work(&ar->wmi_mgmt_tx_work); ++ + time_left = wait_event_timeout(ar->txmgmt_empty_waitq, + (atomic_read(&ar->num_pending_mgmt_tx) == 0), + ATH11K_FLUSH_TIMEOUT); diff --git a/package/kernel/mac80211/patches/nss/ath11k/456-wifi-ath11k-Advertise-TX_QUEUE-mac-hw-flag.patch b/package/kernel/mac80211/patches/nss/ath11k/456-wifi-ath11k-Advertise-TX_QUEUE-mac-hw-flag.patch new file mode 100644 index 00000000000000..510fbc2448cee7 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/456-wifi-ath11k-Advertise-TX_QUEUE-mac-hw-flag.patch @@ -0,0 +1,23 @@ +From dbba58c4f45aecaf2c55a1b2d3500878b86cd8ef Mon Sep 17 00:00:00 2001 +From: Yuvasree Sivasankaran +Date: Mon, 11 Dec 2023 16:02:25 +0530 +Subject: [PATCH] wifi: ath11k: Advertise TX_QUEUE mac hw flag + +To avoid tx queuing in mac80211, advertise TX_QUEUE mac hw flag +which enable tx queuing in driver and avoid performance degradation. + +Signed-off-by: Yuvasree Sivasankaran +--- + drivers/net/wireless/ath/ath11k/mac.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -10141,6 +10141,7 @@ static int __ath11k_mac_register(struct + ieee80211_hw_set(ar->hw, QUEUE_CONTROL); + ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG); + ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK); ++ ieee80211_hw_set(ar->hw, HAS_TX_QUEUE); + + if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET) { + ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD); diff --git a/package/kernel/mac80211/patches/nss/ath11k/457-wifi-ath11k-Avoid-memset-of-ppdu-info-for-next-skb.patch b/package/kernel/mac80211/patches/nss/ath11k/457-wifi-ath11k-Avoid-memset-of-ppdu-info-for-next-skb.patch new file mode 100644 index 00000000000000..21a92296da38a1 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/457-wifi-ath11k-Avoid-memset-of-ppdu-info-for-next-skb.patch @@ -0,0 +1,87 @@ +From 5b4a0de1356558f58df9c6a1f46c7c0ce2fadb03 Mon Sep 17 00:00:00 2001 +From: Yuvasree Sivasankaran +Date: Mon, 28 Aug 2023 14:48:41 +0530 +Subject: [PATCH] ath11k: Avoiding memset of ppdu-info for next skb + +While parsing mon status from skb, ppdu_info got memset with zero during +next skb fetch from queue or mon ring in case a single PPDU is more than +RX_BUFFER_SIZE. Because of this nss value got override for respective +ppdu and leads to warn_on in mac80211.Removed memset from next skb fetch +and added flag to track discontinued skb in case of fetch from skb. + +WARN_ON Reason: + +Rate marked as an HE rate but data is invalid: MCS: 0, NSS: 0 + +Below the call trace observed: + + Call trace: + ieee80211_rx_list+0x1d4/0xcc4 [mac80211] + ieee80211_rx_napi+0x58/0xcc [mac80211] + ath11k_dp_rx_deliver_msdu+0x358/0x3e4 [ath11k] + ath11k_dp_rx_mon_deliver.isra.27+0x470/0x4cc [ath11k] + ath11k_dp_rx_mon_dest_process+0x1cc/0x2bc [ath11k] + ath11k_dp_rx_process_mon_status+0x5ec/0xf90 [ath11k] + ath11k_dp_rx_process_mon_rings+0x40c/0x44c [ath11k] + ath11k_dp_service_srng+0x114/0x2c0 [ath11k] + ath11k_ahb_ext_grp_napi_poll+0x30/0xa0 [ath11k_ahb] + __napi_poll+0x30/0xa4 + net_rx_action+0x118/0x270 + __do_softirq+0x10c/0x244 + irq_exit+0x64/0xb4 + __handle_domain_irq+0x88/0xac + gic_handle_irq+0x74/0xbc + el1_irq+0xf0/0x1c0 + arch_cpu_idle+0x10/0x18 + do_idle+0x104/0x248 + cpu_startup_entry+0x20/0x64 + rest_init+0xd0/0xdc + arch_call_rest_init+0xc/0x14 + start_kernel+0x46c/0x4a4 + --[ end trace e754e9088a240857 ]--- + +Signed-off-by: Yuvasree Sivasankaran +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 6 ++++-- + drivers/net/wireless/ath/ath11k/hal_rx.h | 1 + + 2 files changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -6089,7 +6089,9 @@ int ath11k_dp_rx_process_mon_status(stru + if (!num_buffs_reaped) + goto exit; + +- memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ if (!ppdu_info->ppdu_continuation) ++ memset(ppdu_info, 0, sizeof(*ppdu_info)); ++ + ppdu_info->peer_id = HAL_INVALID_PEERID; + + while ((skb = __skb_dequeue(&skb_list))) { +@@ -6107,7 +6109,6 @@ int ath11k_dp_rx_process_mon_status(stru + if (log_type != ATH11K_PKTLOG_TYPE_INVALID) + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + +- memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); + +@@ -6135,6 +6136,7 @@ int ath11k_dp_rx_process_mon_status(stru + if ((ppdu_info->peer_id == HAL_INVALID_PEERID || + hal_status != HAL_RX_MON_STATUS_PPDU_DONE)) { + dev_kfree_skb_any(skb); ++ ppdu_info->ppdu_continuation = true; + continue; + } + rcu_read_lock(); +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -221,6 +221,7 @@ struct hal_rx_mon_ppdu_info { + u32 num_users; + u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; + struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; ++ bool ppdu_continuation; + }; + + #define HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VALID BIT(30) diff --git a/package/kernel/mac80211/patches/nss/ath11k/458-wifi-ath11k-Add-support-to-send-the-QoS-Null-Data-fr.patch b/package/kernel/mac80211/patches/nss/ath11k/458-wifi-ath11k-Add-support-to-send-the-QoS-Null-Data-fr.patch new file mode 100644 index 00000000000000..88903faddfdbf6 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/458-wifi-ath11k-Add-support-to-send-the-QoS-Null-Data-fr.patch @@ -0,0 +1,30 @@ +From 047c8f3ea16fe1d071f83b51b8c132f797903c91 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Tue, 10 Oct 2023 09:13:38 +0530 +Subject: [PATCH] wifi: ath11k: Add support to send the QoS Null Data frame + through exception path + +When we try to send QoS NULL Data frame in Ethernet encap type, it modified +as QoS Data frame with TID 0 (encryption enabled one if security enabled). +But expectation is, it should be send as open type frame with TID 7 since +its a QoS NULL data frame. + +Added this frame under exception route to bypass TCL with the help of FW. + +Signed-off-by: Karthikeyan Periyasamy +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -221,7 +221,8 @@ tcl_ring_sel: + + switch (ti.encap_type) { + case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: +- if (arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ++ if ((arvif->vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) && ++ ieee80211_is_qos_nullfunc(hdr->frame_control)) + is_diff_encap = true; + else + ath11k_dp_tx_encap_nwifi(skb); diff --git a/package/kernel/mac80211/patches/nss/ath11k/669-ath11k-change-dma_map_single-to-virt_to_phys-in-rx-r.patch b/package/kernel/mac80211/patches/nss/ath11k/669-ath11k-change-dma_map_single-to-virt_to_phys-in-rx-r.patch new file mode 100644 index 00000000000000..06cee635f3e79b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/669-ath11k-change-dma_map_single-to-virt_to_phys-in-rx-r.patch @@ -0,0 +1,66 @@ +From cd0401a0fe82b5f9c31016d11dcc99d9fa4be96d Mon Sep 17 00:00:00 2001 +From: Balamurugan Selvarajan +Date: Wed, 14 Sep 2022 17:12:20 +0530 +Subject: [PATCH] ath11k: Skip cache invalidation in rx replenish + +In ath11k_dp_rxbufs_replenish() the descriptors are updated with +new skb physical address. currently physical address is obtained +using dma_map_single() this api additionally invalidates the cache +lines which is not required in replenish(). This consumes CPU cycles. +In the Rx data path, the desc->skb memory is invalidated before +reading from the memory. So, replace dmap_map_single() with +dma_map_single_attrs(DMA_ATTR_SKIP_CPU_SYNC). This reduces CPU usage by 7%. + +perf top with dma_map_single() +============================= + 24.99% [kernel] [k] __pi___inval_dcache_area + 14.56% [ath11k] [k] ath11k_dp_process_rx + 8.24% [qca_nss_dp] [k] edma_tx_ring_xmit + 5.22% [kernel] [k] dmac_clean_range_no_dsb + 4.26% [kernel] [k] __dma_clean_area_no_dsb + 4.22% [qca_nss_sfe] [k] sfe_recv + 3.90% [kernel] [k] skb_recycler_alloc + 3.87% [kernel] [k] __local_bh_enable_ip + +perf top with dma_map_single_attrs with DMA_ATTR_SKIP_CPU_SYNC +============================================================= + 17.07% [kernel] [k] __pi___inval_dcache_area + 15.53% [ath11k] [k] ath11k_dp_process_rx + 9.03% [qca_nss_dp] [k] edma_tx_ring_xmit + 5.62% [kernel] [k] dmac_clean_range_no_dsb + 5.41% [kernel] [k] skb_recycler_alloc + 4.68% [qca_nss_sfe] [k] sfe_recv + 4.64% [kernel] [k] __dma_clean_area_no_dsb + 3.88% [kernel] [k] __local_bh_enable_ip + +Signed-off-by: Balamurugan Selvarajan +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -392,9 +392,9 @@ int ath11k_dp_rxbufs_replenish(struct at + skb->data); + } + +- paddr = dma_map_single(ab->dev, skb->data, +- skb->len + skb_tailroom(skb), +- DMA_FROM_DEVICE); ++ paddr = dma_map_single_attrs(ab->dev, skb->data, ++ skb->len + skb_tailroom(skb), ++ DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + +@@ -430,8 +430,8 @@ fail_idr_remove: + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + fail_dma_unmap: +- dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), +- DMA_FROM_DEVICE); ++ dma_unmap_single_attrs(ab->dev, paddr, skb->len + skb_tailroom(skb), ++ DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + fail_free_skb: + dev_kfree_skb_any(skb); + diff --git a/package/kernel/mac80211/patches/nss/ath11k/907-01-wifi-ath11k-remove-invalid-peer-create-logic.patch b/package/kernel/mac80211/patches/nss/ath11k/907-01-wifi-ath11k-remove-invalid-peer-create-logic.patch new file mode 100644 index 00000000000000..4d8f9f28e3a1e3 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/907-01-wifi-ath11k-remove-invalid-peer-create-logic.patch @@ -0,0 +1,58 @@ +Received: from bqiang-Celadon-RN.qca.qualcomm.com (10.80.80.8) by +From: Baochen Qiang +To: +Subject: [PATCH 1/4] wifi: ath11k: remove invalid peer create logic +Date: Tue, 23 Jan 2024 10:56:57 +0800 + +In ath11k_mac_op_assign_vif_chanctx(), there is a logic to +create peer using ar->mac_addr for a STA vdev. This is invalid +because a STA vdev should have a peer created using AP's +MAC address. Besides, if we run into that logic, it means a peer +has already been created earlier, we should not create it again. +So remove it. + +This is found during code review. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Baochen Qiang +Acked-by: Jeff Johnson +--- + drivers/net/wireless/ath/ath11k/mac.c | 16 ---------------- + 1 file changed, 16 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8218,7 +8218,6 @@ ath11k_mac_op_assign_vif_chanctx(struct + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + int ret; +- struct peer_create_params param; + + mutex_lock(&ar->conf_mutex); + +@@ -8241,21 +8240,6 @@ ath11k_mac_op_assign_vif_chanctx(struct + goto out; + } + +- if (ab->hw_params.vdev_start_delay && +- arvif->vdev_type != WMI_VDEV_TYPE_AP && +- arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) { +- param.vdev_id = arvif->vdev_id; +- param.peer_type = WMI_PEER_TYPE_DEFAULT; +- param.peer_addr = ar->mac_addr; +- +- ret = ath11k_peer_create(ar, arvif, NULL, ¶m); +- if (ret) { +- ath11k_warn(ab, "failed to create peer after vdev start delay: %d", +- ret); +- goto out; +- } +- } +- + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_mac_monitor_start(ar); + if (ret) { diff --git a/package/kernel/mac80211/patches/nss/ath11k/907-02-wifi-ath11k-rename-ath11k_start_vdev_delay.patch b/package/kernel/mac80211/patches/nss/ath11k/907-02-wifi-ath11k-rename-ath11k_start_vdev_delay.patch new file mode 100644 index 00000000000000..7d04e371edd5fa --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/907-02-wifi-ath11k-rename-ath11k_start_vdev_delay.patch @@ -0,0 +1,52 @@ +From: Baochen Qiang +To: +Subject: [PATCH 2/4] wifi: ath11k: rename ath11k_start_vdev_delay() +Date: Tue, 23 Jan 2024 10:56:58 +0800 + +Rename ath11k_start_vdev_delay() as ath11k_mac_start_vdev_delay() +to follow naming convention. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Baochen Qiang +Acked-by: Jeff Johnson +--- + drivers/net/wireless/ath/ath11k/mac.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -256,8 +256,8 @@ static const u32 ath11k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, + }; + +-static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, +- struct ieee80211_vif *vif); ++static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif); + + enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) + { +@@ -5314,7 +5314,7 @@ static int ath11k_mac_station_add(struct + if (ab->hw_params.vdev_start_delay && + !arvif->is_started && + arvif->vdev_type != WMI_VDEV_TYPE_AP) { +- ret = ath11k_start_vdev_delay(ar->hw, vif); ++ ret = ath11k_mac_start_vdev_delay(ar->hw, vif); + if (ret) { + ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); + goto free_tx_stats; +@@ -8161,8 +8161,8 @@ unlock: + mutex_unlock(&ar->conf_mutex); + } + +-static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, +- struct ieee80211_vif *vif) ++static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) + { + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; diff --git a/package/kernel/mac80211/patches/nss/ath11k/907-03-wifi-ath11k-avoid-forward-declaration-of-ath11k_mac_start_vdev_delay.patch b/package/kernel/mac80211/patches/nss/ath11k/907-03-wifi-ath11k-avoid-forward-declaration-of-ath11k_mac_start_vdev_delay.patch new file mode 100644 index 00000000000000..f928d851a678d7 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/907-03-wifi-ath11k-avoid-forward-declaration-of-ath11k_mac_start_vdev_delay.patch @@ -0,0 +1,601 @@ +From: Baochen Qiang +To: +Subject: [PATCH 3/4] wifi: ath11k: avoid forward declaration of + ath11k_mac_start_vdev_delay() +Date: Tue, 23 Jan 2024 10:56:59 +0800 + +Currently ath11k_mac_start_vdev_delay() needs a forward declaration because +it is defined after where it is called. Avoid this by re-arranging +ath11k_mac_station_add() and ath11k_mac_op_sta_state(). + +No functional changes. Compile tested only. + +Signed-off-by: Baochen Qiang +Acked-by: Jeff Johnson +--- + drivers/net/wireless/ath/ath11k/mac.c | 459 +++++++++++++------------- + 1 file changed, 228 insertions(+), 231 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -256,9 +256,6 @@ static const u32 ath11k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, + }; + +-static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw, +- struct ieee80211_vif *vif); +- + enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy) + { + enum nl80211_he_ru_alloc ret; +@@ -5244,100 +5241,6 @@ static void ath11k_mac_dec_num_stations( + ar->num_stations--; + } + +-static int ath11k_mac_station_add(struct ath11k *ar, +- struct ieee80211_vif *vif, +- struct ieee80211_sta *sta) +-{ +- struct ath11k_base *ab = ar->ab; +- struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); +- struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); +- struct peer_create_params peer_param; +- int ret; +- +- lockdep_assert_held(&ar->conf_mutex); +- +- ret = ath11k_mac_inc_num_stations(arvif, sta); +- if (ret) { +- ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", +- ar->max_num_stations); +- goto exit; +- } +- +- arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); +- if (!arsta->rx_stats) { +- ret = -ENOMEM; +- goto dec_num_station; +- } +- +- peer_param.vdev_id = arvif->vdev_id; +- peer_param.peer_addr = sta->addr; +- peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; +- +- ret = ath11k_peer_create(ar, arvif, sta, &peer_param); +- if (ret) { +- ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- goto free_rx_stats; +- } +- +- ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- +- if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) { +- arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); +- if (!arsta->tx_stats) { +- ret = -ENOMEM; +- goto free_peer; +- } +- } +- +- if (ieee80211_vif_is_mesh(vif)) { +- ath11k_dbg(ab, ATH11K_DBG_MAC, +- "setting USE_4ADDR for mesh STA %pM\n", sta->addr); +- ret = ath11k_wmi_set_peer_param(ar, sta->addr, +- arvif->vdev_id, +- WMI_PEER_USE_4ADDR, 1); +- if (ret) { +- ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n", +- sta->addr, ret); +- goto free_tx_stats; +- } +- } +- +- ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); +- if (ret) { +- ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", +- sta->addr, arvif->vdev_id, ret); +- goto free_tx_stats; +- } +- +- if (ab->hw_params.vdev_start_delay && +- !arvif->is_started && +- arvif->vdev_type != WMI_VDEV_TYPE_AP) { +- ret = ath11k_mac_start_vdev_delay(ar->hw, vif); +- if (ret) { +- ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); +- goto free_tx_stats; +- } +- } +- +- ewma_avg_rssi_init(&arsta->avg_rssi); +- return 0; +- +-free_tx_stats: +- kfree(arsta->tx_stats); +- arsta->tx_stats = NULL; +-free_peer: +- ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +-free_rx_stats: +- kfree(arsta->rx_stats); +- arsta->rx_stats = NULL; +-dec_num_station: +- ath11k_mac_dec_num_stations(arvif, sta); +-exit: +- return ret; +-} +- + static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, + struct ieee80211_sta *sta) + { +@@ -5393,187 +5296,6 @@ static int ath11k_mac_cfg_dyn_vlan(struc + return ret; + } + +-static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, +- struct ieee80211_vif *vif, +- struct ieee80211_sta *sta, +- enum ieee80211_sta_state old_state, +- enum ieee80211_sta_state new_state) +-{ +- struct ath11k *ar = hw->priv; +- struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); +- struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); +- struct ath11k_peer *peer; +- int ret = 0; +- +- /* cancel must be done outside the mutex to avoid deadlock */ +- if ((old_state == IEEE80211_STA_NONE && +- new_state == IEEE80211_STA_NOTEXIST)) { +- cancel_work_sync(&arsta->update_wk); +- cancel_work_sync(&arsta->set_4addr_wk); +- } +- +- mutex_lock(&ar->conf_mutex); +- +- if (old_state == IEEE80211_STA_NOTEXIST && +- new_state == IEEE80211_STA_NONE) { +- memset(arsta, 0, sizeof(*arsta)); +- arsta->arvif = arvif; +- arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; +- INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); +- INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk); +- +- ret = ath11k_mac_station_add(ar, vif, sta); +- if (ret) +- ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- } else if ((old_state == IEEE80211_STA_NONE && +- new_state == IEEE80211_STA_NOTEXIST)) { +- bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && +- vif->type == NL80211_IFTYPE_STATION; +- +- ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); +- +- if (!skip_peer_delete) { +- ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +- if (ret) +- ath11k_warn(ar->ab, +- "Failed to delete peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- else +- ath11k_dbg(ar->ab, +- ATH11K_DBG_MAC, +- "Removed peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- } +- +- ath11k_mac_dec_num_stations(arvif, sta); +- mutex_lock(&ar->ab->tbl_mtx_lock); +- spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (skip_peer_delete && peer) { +- peer->sta = NULL; +- } else if (peer && peer->sta == sta) { +- ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", +- vif->addr, arvif->vdev_id); +- ath11k_peer_rhash_delete(ar->ab, peer); +- peer->sta = NULL; +- list_del(&peer->list); +- kfree(peer); +- ar->num_peers--; +- } +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); +- +- kfree(arsta->tx_stats); +- arsta->tx_stats = NULL; +- +- kfree(arsta->rx_stats); +- arsta->rx_stats = NULL; +- } else if (old_state == IEEE80211_STA_AUTH && +- new_state == IEEE80211_STA_ASSOC && +- (vif->type == NL80211_IFTYPE_AP || +- vif->type == NL80211_IFTYPE_MESH_POINT || +- vif->type == NL80211_IFTYPE_ADHOC)) { +- ret = ath11k_station_assoc(ar, vif, sta, false); +- if (ret) +- ath11k_warn(ar->ab, "Failed to associate station: %pM\n", +- sta->addr); +- +- spin_lock_bh(&ar->data_lock); +- /* Set arsta bw and prev bw */ +- arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); +- arsta->bw_prev = arsta->bw; +- spin_unlock_bh(&ar->data_lock); +- +- /* Driver should clear the peer keys during mac80211's ref ptr +- * gets cleared in __sta_info_destroy_part2 (trans from +- * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC) +- */ +- ret = ath11k_clear_peer_keys(arvif, sta->addr); +- if (ret) { +- ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", +- arvif->vdev_id, ret); +- return ret; +- } +- } else if (old_state == IEEE80211_STA_ASSOC && +- new_state == IEEE80211_STA_AUTHORIZED) { +- spin_lock_bh(&ar->ab->base_lock); +- +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (peer) +- peer->is_authorized = true; +- +- spin_unlock_bh(&ar->ab->base_lock); +- +- if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { +- ret = ath11k_wmi_set_peer_param(ar, sta->addr, +- arvif->vdev_id, +- WMI_PEER_AUTHORIZE, +- 1); +- if (ret) +- ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", +- sta->addr, arvif->vdev_id, ret); +- } else if (ar->ab->nss.enabled && +- vif->type == NL80211_IFTYPE_AP_VLAN && +- !arsta->use_4addr_set) { +- +- if (ar->state == ATH11K_STATE_RESTARTED) { +- /* During ieee80211_reconfig(), at this point, nss ext vdev peer is not +- * authorized. Hence ath11k_mac_cfg_dyn_vlan() will return error. We save +- * the state vars here and use it later once nss ext vdev is authorized +- * in ath11k_mac_op_set_key() */ +- struct ath11k_dyn_vlan_cfg *ar_dyn_vlan_cfg; +- ar_dyn_vlan_cfg = kzalloc(sizeof(*ar_dyn_vlan_cfg), GFP_ATOMIC); +- +- if (!ar_dyn_vlan_cfg) { +- ath11k_warn(ar->ab, "failed to save state for dynamic AP_VLAN configuration (%d)", +- -ENOSPC); +- } else { +- INIT_LIST_HEAD(&ar_dyn_vlan_cfg->cfg_list); +- ar_dyn_vlan_cfg->arvif = arvif; +- ar_dyn_vlan_cfg->sta = sta; +- /* save it to arvif (AP_VLAN) list */ +- list_add_tail(&ar_dyn_vlan_cfg->cfg_list, &arvif->dyn_vlan_cfg); +- } +- } else { +- ret = ath11k_mac_cfg_dyn_vlan(ar->ab, arvif, sta); +- if (ret) +- ath11k_warn(ar->ab, "failed to cfg dyn vlan for peer %pM: %d\n", +- sta->addr, ret); +- } +- } +- } else if (old_state == IEEE80211_STA_AUTHORIZED && +- new_state == IEEE80211_STA_ASSOC) { +- +- spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (peer) +- peer->is_authorized = false; +- spin_unlock_bh(&ar->ab->base_lock); +- } else if (old_state == IEEE80211_STA_AUTHORIZED && +- new_state == IEEE80211_STA_ASSOC) { +- spin_lock_bh(&ar->ab->base_lock); +- +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (peer) +- peer->is_authorized = false; +- +- spin_unlock_bh(&ar->ab->base_lock); +- } else if (old_state == IEEE80211_STA_ASSOC && +- new_state == IEEE80211_STA_AUTH && +- (vif->type == NL80211_IFTYPE_AP || +- vif->type == NL80211_IFTYPE_MESH_POINT || +- vif->type == NL80211_IFTYPE_ADHOC)) { +- ret = ath11k_station_disassoc(ar, vif, sta); +- if (ret) +- ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", +- sta->addr); +- } +- +- mutex_unlock(&ar->conf_mutex); +- return ret; +-} +- + static int ath11k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +@@ -9738,6 +9460,281 @@ ath11k_mac_op_config_mesh_offload_path(s + } + #endif + ++static int ath11k_mac_station_add(struct ath11k *ar, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); ++ struct peer_create_params peer_param; ++ int ret; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ ret = ath11k_mac_inc_num_stations(arvif, sta); ++ if (ret) { ++ ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n", ++ ar->max_num_stations); ++ goto exit; ++ } ++ ++ arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); ++ if (!arsta->rx_stats) { ++ ret = -ENOMEM; ++ goto dec_num_station; ++ } ++ ++ peer_param.vdev_id = arvif->vdev_id; ++ peer_param.peer_addr = sta->addr; ++ peer_param.peer_type = WMI_PEER_TYPE_DEFAULT; ++ ++ ret = ath11k_peer_create(ar, arvif, sta, &peer_param); ++ if (ret) { ++ ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ goto free_rx_stats; ++ } ++ ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ ++ if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) { ++ arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); ++ if (!arsta->tx_stats) { ++ ret = -ENOMEM; ++ goto free_peer; ++ } ++ } ++ ++ if (ieee80211_vif_is_mesh(vif)) { ++ ath11k_dbg(ab, ATH11K_DBG_MAC, ++ "setting USE_4ADDR for mesh STA %pM\n", sta->addr); ++ ret = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_USE_4ADDR, 1); ++ if (ret) { ++ ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n", ++ sta->addr, ret); ++ goto free_tx_stats; ++ } ++ } ++ ++ ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr); ++ if (ret) { ++ ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n", ++ sta->addr, arvif->vdev_id, ret); ++ goto free_tx_stats; ++ } ++ ++ if (ab->hw_params.vdev_start_delay && ++ !arvif->is_started && ++ arvif->vdev_type != WMI_VDEV_TYPE_AP) { ++ ret = ath11k_mac_start_vdev_delay(ar->hw, vif); ++ if (ret) { ++ ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); ++ goto free_tx_stats; ++ } ++ } ++ ++ ewma_avg_rssi_init(&arsta->avg_rssi); ++ return 0; ++ ++free_tx_stats: ++ kfree(arsta->tx_stats); ++ arsta->tx_stats = NULL; ++free_peer: ++ ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); ++free_rx_stats: ++ kfree(arsta->rx_stats); ++ arsta->rx_stats = NULL; ++dec_num_station: ++ ath11k_mac_dec_num_stations(arvif, sta); ++exit: ++ return ret; ++} ++ ++static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta, ++ enum ieee80211_sta_state old_state, ++ enum ieee80211_sta_state new_state) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); ++ struct ath11k_peer *peer; ++ int ret = 0; ++ ++ /* cancel must be done outside the mutex to avoid deadlock */ ++ if ((old_state == IEEE80211_STA_NONE && ++ new_state == IEEE80211_STA_NOTEXIST)) { ++ cancel_work_sync(&arsta->update_wk); ++ cancel_work_sync(&arsta->set_4addr_wk); ++ } ++ ++ mutex_lock(&ar->conf_mutex); ++ ++ if (old_state == IEEE80211_STA_NOTEXIST && ++ new_state == IEEE80211_STA_NONE) { ++ memset(arsta, 0, sizeof(*arsta)); ++ arsta->arvif = arvif; ++ arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED; ++ INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk); ++ INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk); ++ ++ ret = ath11k_mac_station_add(ar, vif, sta); ++ if (ret) ++ ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ } else if ((old_state == IEEE80211_STA_NONE && ++ new_state == IEEE80211_STA_NOTEXIST)) { ++ bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && ++ vif->type == NL80211_IFTYPE_STATION; ++ ++ ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); ++ ++ if (!skip_peer_delete) { ++ ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); ++ if (ret) ++ ath11k_warn(ar->ab, ++ "Failed to delete peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ else ++ ath11k_dbg(ar->ab, ++ ATH11K_DBG_MAC, ++ "Removed peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ } ++ ++ ath11k_mac_dec_num_stations(arvif, sta); ++ mutex_lock(&ar->ab->tbl_mtx_lock); ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (skip_peer_delete && peer) { ++ peer->sta = NULL; ++ } else if (peer && peer->sta == sta) { ++ ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", ++ vif->addr, arvif->vdev_id); ++ ath11k_peer_rhash_delete(ar->ab, peer); ++ peer->sta = NULL; ++ list_del(&peer->list); ++ kfree(peer); ++ ar->num_peers--; ++ } ++ spin_unlock_bh(&ar->ab->base_lock); ++ mutex_unlock(&ar->ab->tbl_mtx_lock); ++ ++ kfree(arsta->tx_stats); ++ arsta->tx_stats = NULL; ++ ++ kfree(arsta->rx_stats); ++ arsta->rx_stats = NULL; ++ } else if (old_state == IEEE80211_STA_AUTH && ++ new_state == IEEE80211_STA_ASSOC && ++ (vif->type == NL80211_IFTYPE_AP || ++ vif->type == NL80211_IFTYPE_MESH_POINT || ++ vif->type == NL80211_IFTYPE_ADHOC)) { ++ ret = ath11k_station_assoc(ar, vif, sta, false); ++ if (ret) ++ ath11k_warn(ar->ab, "Failed to associate station: %pM\n", ++ sta->addr); ++ ++ spin_lock_bh(&ar->data_lock); ++ /* Set arsta bw and prev bw */ ++ arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); ++ arsta->bw_prev = arsta->bw; ++ spin_unlock_bh(&ar->data_lock); ++ ++ /* Driver should clear the peer keys during mac80211's ref ptr ++ * gets cleared in __sta_info_destroy_part2 (trans from ++ * IEEE80211_STA_AUTHORIZED to IEEE80211_STA_ASSOC) ++ */ ++ ret = ath11k_clear_peer_keys(arvif, sta->addr); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to clear all peer keys for vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ } else if (old_state == IEEE80211_STA_ASSOC && ++ new_state == IEEE80211_STA_AUTHORIZED) { ++ spin_lock_bh(&ar->ab->base_lock); ++ ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = true; ++ ++ spin_unlock_bh(&ar->ab->base_lock); ++ ++ if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) { ++ ret = ath11k_wmi_set_peer_param(ar, sta->addr, ++ arvif->vdev_id, ++ WMI_PEER_AUTHORIZE, ++ 1); ++ if (ret) ++ ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n", ++ sta->addr, arvif->vdev_id, ret); ++ } else if (ar->ab->nss.enabled && ++ vif->type == NL80211_IFTYPE_AP_VLAN && ++ !arsta->use_4addr_set) { ++ ++ if (ar->state == ATH11K_STATE_RESTARTED) { ++ /* During ieee80211_reconfig(), at this point, nss ext vdev peer is not ++ * authorized. Hence ath11k_mac_cfg_dyn_vlan() will return error. We save ++ * the state vars here and use it later once nss ext vdev is authorized ++ * in ath11k_mac_op_set_key() */ ++ struct ath11k_dyn_vlan_cfg *ar_dyn_vlan_cfg; ++ ar_dyn_vlan_cfg = kzalloc(sizeof(*ar_dyn_vlan_cfg), GFP_ATOMIC); ++ ++ if (!ar_dyn_vlan_cfg) { ++ ath11k_warn(ar->ab, "failed to save state for dynamic AP_VLAN configuration (%d)", ++ -ENOSPC); ++ } else { ++ INIT_LIST_HEAD(&ar_dyn_vlan_cfg->cfg_list); ++ ar_dyn_vlan_cfg->arvif = arvif; ++ ar_dyn_vlan_cfg->sta = sta; ++ /* save it to arvif (AP_VLAN) list */ ++ list_add_tail(&ar_dyn_vlan_cfg->cfg_list, &arvif->dyn_vlan_cfg); ++ } ++ } else { ++ ret = ath11k_mac_cfg_dyn_vlan(ar->ab, arvif, sta); ++ if (ret) ++ ath11k_warn(ar->ab, "failed to cfg dyn vlan for peer %pM: %d\n", ++ sta->addr, ret); ++ } ++ } ++ } else if (old_state == IEEE80211_STA_AUTHORIZED && ++ new_state == IEEE80211_STA_ASSOC) { ++ ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = false; ++ spin_unlock_bh(&ar->ab->base_lock); ++ } else if (old_state == IEEE80211_STA_AUTHORIZED && ++ new_state == IEEE80211_STA_ASSOC) { ++ spin_lock_bh(&ar->ab->base_lock); ++ ++ peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ if (peer) ++ peer->is_authorized = false; ++ ++ spin_unlock_bh(&ar->ab->base_lock); ++ } else if (old_state == IEEE80211_STA_ASSOC && ++ new_state == IEEE80211_STA_AUTH && ++ (vif->type == NL80211_IFTYPE_AP || ++ vif->type == NL80211_IFTYPE_MESH_POINT || ++ vif->type == NL80211_IFTYPE_ADHOC)) { ++ ret = ath11k_station_disassoc(ar, vif, sta); ++ if (ret) ++ ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", ++ sta->addr); ++ } ++ ++ mutex_unlock(&ar->conf_mutex); ++ return ret; ++} ++ + static const struct ieee80211_ops ath11k_ops = { + .tx = ath11k_mac_op_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, diff --git a/package/kernel/mac80211/patches/nss/ath11k/907-04-wifi-ath11k-fix-connection-failure-due-to-unexpected-peer-delete.patch b/package/kernel/mac80211/patches/nss/ath11k/907-04-wifi-ath11k-fix-connection-failure-due-to-unexpected-peer-delete.patch new file mode 100644 index 00000000000000..010825fd16656e --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/907-04-wifi-ath11k-fix-connection-failure-due-to-unexpected-peer-delete.patch @@ -0,0 +1,257 @@ +From: Baochen Qiang +To: +Subject: [PATCH 4/4] wifi: ath11k: fix connection failure due to unexpected + peer delete +Date: Tue, 23 Jan 2024 10:57:00 +0800 + +Currently ath11k_mac_op_unassign_vif_chanctx() deletes peer but +ath11k_mac_op_assign_vif_chanctx() doesn't create it. This results in +connection failure if MAC80211 calls drv_unassign_vif_chanctx() and +drv_assign_vif_chanctx() during AUTH and ASSOC, see below log: + +[ 102.372431] wlan0: authenticated +[ 102.372585] ath11k_pci 0000:01:00.0: wlan0: disabling HT/VHT/HE as WMM/QoS is not supported by the AP +[ 102.372593] ath11k_pci 0000:01:00.0: mac chanctx unassign ptr ffff895084638598 vdev_id 0 +[ 102.372808] ath11k_pci 0000:01:00.0: WMI vdev stop id 0x0 +[ 102.383114] ath11k_pci 0000:01:00.0: vdev stopped for vdev id 0 +[ 102.384689] ath11k_pci 0000:01:00.0: WMI peer delete vdev_id 0 peer_addr 20:e5:2a:21:c4:51 +[ 102.396676] ath11k_pci 0000:01:00.0: htt peer unmap vdev 0 peer 20:e5:2a:21:c4:51 id 3 +[ 102.396711] ath11k_pci 0000:01:00.0: peer delete resp for vdev id 0 addr 20:e5:2a:21:c4:51 +[ 102.396722] ath11k_pci 0000:01:00.0: mac removed peer 20:e5:2a:21:c4:51 vdev 0 after vdev stop +[ 102.396780] ath11k_pci 0000:01:00.0: mac chanctx assign ptr ffff895084639c18 vdev_id 0 +[ 102.400628] wlan0: associate with 20:e5:2a:21:c4:51 (try 1/3) +[ 102.508864] wlan0: associate with 20:e5:2a:21:c4:51 (try 2/3) +[ 102.612815] wlan0: associate with 20:e5:2a:21:c4:51 (try 3/3) +[ 102.720846] wlan0: association with 20:e5:2a:21:c4:51 timed out + +The peer delete logic in ath11k_mac_op_unassign_vif_chanctx() is +introduced by commit b4a0f54156ac ("ath11k: move peer delete after +vdev stop of station for QCA6390 and WCN6855") to fix firmware +crash issue caused by unexpected vdev stop/peer delete sequence. + +Actually for a STA interface peer should be deleted in +ath11k_mac_op_sta_state() when STA's state changes from +IEEE80211_STA_NONE to IEEE80211_STA_NOTEXIST, which also coincides +with current peer creation design that peer is created during +IEEE80211_STA_NOTEXIST -> IEEE80211_STA_NONE transition. So move +peer delete back to ath11k_mac_op_sta_state(), also stop vdev before +deleting peer to fix the firmware crash issue mentioned there. In +this way the connection failure mentioned here is also fixed. + +Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 +Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23 +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +Fixes: b4a0f54156ac ("ath11k: move peer delete after vdev stop of station for QCA6390 and WCN6855") +Signed-off-by: Baochen Qiang +Acked-by: Jeff Johnson +--- + drivers/net/wireless/ath/ath11k/mac.c | 139 ++++++++++++++++---------- + 1 file changed, 85 insertions(+), 54 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -7930,6 +7930,30 @@ static int ath11k_mac_start_vdev_delay(s + return 0; + } + ++static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif) ++{ ++ struct ath11k *ar = hw->priv; ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ int ret; ++ ++ if (WARN_ON(!arvif->is_started)) ++ return -EBUSY; ++ ++ ret = ath11k_mac_vdev_stop(arvif); ++ if (ret) { ++ ath11k_warn(ab, "failed to stop vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ ++ arvif->is_started = false; ++ ++ /* TODO: Setup ps and cts/rts protection */ ++ return 0; ++} ++ + static int + ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, +@@ -7974,15 +7998,17 @@ ath11k_mac_op_assign_vif_chanctx(struct + goto out; + } + +- ret = ath11k_mac_vdev_start(arvif, ctx); +- if (ret) { +- ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", +- arvif->vdev_id, vif->addr, +- ctx->def.chan->center_freq, ret); +- goto out; +- } ++ if (!arvif->is_started) { ++ ret = ath11k_mac_vdev_start(arvif, ctx); ++ if (ret) { ++ ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", ++ arvif->vdev_id, vif->addr, ++ ctx->def.chan->center_freq, ret); ++ goto out; ++ } + +- arvif->is_started = true; ++ arvif->is_started = true; ++ } + + if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && + test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) { +@@ -8022,8 +8048,6 @@ ath11k_mac_op_unassign_vif_chanctx(struc + "chanctx unassign ptr %p vdev_id %i\n", + ctx, arvif->vdev_id); + +- WARN_ON(!arvif->is_started); +- + if (ab->hw_params.vdev_start_delay && + arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + spin_lock_bh(&ab->base_lock); +@@ -8047,24 +8071,13 @@ ath11k_mac_op_unassign_vif_chanctx(struc + return; + } + +- ret = ath11k_mac_vdev_stop(arvif); +- if (ret) +- ath11k_warn(ab, "failed to stop vdev %i: %d\n", +- arvif->vdev_id, ret); +- +- arvif->is_started = false; +- +- if (ab->hw_params.vdev_start_delay && +- arvif->vdev_type == WMI_VDEV_TYPE_STA) { +- ret = ath11k_peer_delete(ar, arvif->vdev_id, arvif->bssid); ++ if (arvif->is_started) { ++ ret = ath11k_mac_vdev_stop(arvif); + if (ret) +- ath11k_warn(ar->ab, +- "failed to delete peer %pM for vdev %d: %d\n", +- arvif->bssid, arvif->vdev_id, ret); +- else +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, +- "removed peer %pM vdev %d after vdev stop\n", +- arvif->bssid, arvif->vdev_id); ++ ath11k_warn(ab, "failed to stop vdev %i: %d\n", ++ arvif->vdev_id, ret); ++ ++ arvif->is_started = false; + } + + if (ab->hw_params.vdev_start_delay && +@@ -9554,6 +9567,46 @@ exit: + return ret; + } + ++static int ath11k_mac_station_remove(struct ath11k *ar, ++ struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct ath11k_base *ab = ar->ab; ++ struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); ++ struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta); ++ int ret; ++ ++ if (ab->hw_params.vdev_start_delay && ++ arvif->is_started && ++ arvif->vdev_type != WMI_VDEV_TYPE_AP) { ++ ret = ath11k_mac_stop_vdev_early(ar->hw, vif); ++ if (ret) { ++ ath11k_warn(ab, "failed to do early vdev stop: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); ++ ++ ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); ++ if (ret) ++ ath11k_warn(ab, "Failed to delete peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ else ++ ath11k_dbg(ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); ++ ++ ath11k_mac_dec_num_stations(arvif, sta); ++ ++ kfree(arsta->tx_stats); ++ arsta->tx_stats = NULL; ++ ++ kfree(arsta->rx_stats); ++ arsta->rx_stats = NULL; ++ ++ return ret; ++} ++ + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -9589,31 +9642,15 @@ static int ath11k_mac_op_sta_state(struc + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { +- bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay && +- vif->type == NL80211_IFTYPE_STATION; +- +- ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); +- +- if (!skip_peer_delete) { +- ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr); +- if (ret) +- ath11k_warn(ar->ab, +- "Failed to delete peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- else +- ath11k_dbg(ar->ab, +- ATH11K_DBG_MAC, +- "Removed peer: %pM for VDEV: %d\n", +- sta->addr, arvif->vdev_id); +- } ++ ret = ath11k_mac_station_remove(ar, vif, sta); ++ if (ret) ++ ath11k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n", ++ sta->addr, arvif->vdev_id); + +- ath11k_mac_dec_num_stations(arvif, sta); + mutex_lock(&ar->ab->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); +- if (skip_peer_delete && peer) { +- peer->sta = NULL; +- } else if (peer && peer->sta == sta) { ++ if (peer && peer->sta == sta) { + ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n", + vif->addr, arvif->vdev_id); + ath11k_peer_rhash_delete(ar->ab, peer); +@@ -9624,12 +9661,6 @@ static int ath11k_mac_op_sta_state(struc + } + spin_unlock_bh(&ar->ab->base_lock); + mutex_unlock(&ar->ab->tbl_mtx_lock); +- +- kfree(arsta->tx_stats); +- arsta->tx_stats = NULL; +- +- kfree(arsta->rx_stats); +- arsta->rx_stats = NULL; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || +@@ -10202,6 +10233,8 @@ static int __ath11k_mac_register(struct + + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + ++ wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); ++ + ar->hw->queues = ATH11K_HW_MAX_QUEUES; + ar->hw->wiphy->tx_queue_len = ATH11K_QUEUE_LEN; + ar->hw->offchannel_tx_hw_queue = ATH11K_HW_MAX_QUEUES - 1; diff --git a/package/kernel/mac80211/patches/nss/ath11k/908-ath11k-make-debugfs-sta-htt-stats-modular.patch b/package/kernel/mac80211/patches/nss/ath11k/908-ath11k-make-debugfs-sta-htt-stats-modular.patch new file mode 100644 index 00000000000000..4276e64cc30364 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/908-ath11k-make-debugfs-sta-htt-stats-modular.patch @@ -0,0 +1,198 @@ +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -80,6 +80,24 @@ config ATH11K_DEBUGFS + + If unsure, say Y to make it easier to debug problems. + ++config ATH11K_DEBUGFS_STA ++ bool "QCA ath11k debugfs STA support" ++ depends on ATH11K_DEBUGFS ++ default n ++ help ++ Enable ath11k debugfs STA support ++ ++ If unsure, say Y to make it easier to debug problems. ++ ++config ATH11K_DEBUGFS_HTT_STATS ++ bool "QCA ath11k debugfs HTT stats support" ++ depends on ATH11K_DEBUGFS ++ default n ++ help ++ Enable ath11k debugfs HTT stats support ++ ++ If unsure, say Y to make it easier to debug problems. ++ + config ATH11K_TRACING + bool "ath11k tracing support" + depends on ATH11K && EVENT_TRACING +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -19,7 +19,9 @@ ath11k-y += core.o \ + hw.o \ + pcic.o + +-ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ++ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o ++ath11k-$(CPTCFG_ATH11K_DEBUGFS_STA) += debugfs_sta.o ++ath11k-$(CPTCFG_ATH11K_DEBUGFS_HTT_STATS) += debugfs_htt_stats.o + ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o + ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o + ath11k-$(CPTCFG_ATH11K_THERMAL) += thermal.o +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -1875,7 +1875,9 @@ int ath11k_debugfs_register(struct ath11 + snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev); + debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf); + ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + ath11k_debugfs_htt_stats_init(ar); ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + ath11k_debugfs_fw_stats_init(ar); + +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -11,7 +11,9 @@ + #include "debug.h" + #include "dp_tx.h" + #include "dp_rx.h" ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + #include "debugfs_htt_stats.h" ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + static inline u32 ath11k_he_tones_in_ru_to_nl80211_he_ru_alloc(u16 ru_tones) + { +@@ -551,6 +553,7 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + static int + ath11k_dbg_sta_open_htt_peer_stats(struct inode *inode, struct file *file) + { +@@ -622,6 +625,7 @@ static const struct file_operations fops + .owner = THIS_MODULE, + .llseek = default_llseek, + }; ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + static ssize_t ath11k_dbg_sta_write_peer_pktlog(struct file *file, + const char __user *buf, +@@ -906,6 +910,7 @@ static const struct file_operations fops + .llseek = default_llseek, + }; + ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + static ssize_t + ath11k_write_htt_peer_stats_reset(struct file *file, + const char __user *user_buf, +@@ -965,6 +970,7 @@ static const struct file_operations fops + .owner = THIS_MODULE, + .llseek = default_llseek, + }; ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + static ssize_t ath11k_dbg_sta_read_peer_ps_state(struct file *file, + char __user *user_buf, +@@ -1111,8 +1117,10 @@ void ath11k_debugfs_sta_op_add(struct ie + &fops_reset_rx_stats); + } + ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + debugfs_create_file("htt_peer_stats", 0400, dir, sta, + &fops_htt_peer_stats); ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + debugfs_create_file("peer_pktlog", 0644, dir, sta, + &fops_peer_pktlog); +@@ -1122,10 +1130,12 @@ void ath11k_debugfs_sta_op_add(struct ie + debugfs_create_file("addba_resp", 0200, dir, sta, &fops_addba_resp); + debugfs_create_file("delba", 0200, dir, sta, &fops_delba); + ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + if (test_bit(WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET, + ar->ab->wmi_ab.svc_map)) + debugfs_create_file("htt_peer_stats_reset", 0600, dir, sta, + &fops_htt_peer_stats_reset); ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + + debugfs_create_file("peer_ps_state", 0400, dir, sta, + &fops_peer_ps_state); +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1723,8 +1723,10 @@ ath11k_update_per_peer_tx_stats(struct a + peer_stats->mu_pos = mu_pos; + peer_stats->ru_tones = arsta->txrate.he_ru_alloc; + ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) + ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); ++#endif + } + + usr_stats->rate_stats_updated = true; +@@ -2170,7 +2172,9 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + ath11k_htt_pull_ppdu_stats(ab, skb); + break; + case HTT_T2H_MSG_TYPE_EXT_STATS_CONF: ++#ifdef CPTCFG_ATH11K_DEBUGFS_HTT_STATS + ath11k_debugfs_htt_ext_stats_handler(ab, skb); ++#endif /* CPTCFG_ATH11K_DEBUGFS_HTT_STATS */ + break; + case HTT_T2H_MSG_TYPE_PKTLOG: + ath11k_htt_pktlog(ab, skb); +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -7,7 +7,9 @@ + #include "core.h" + #include "dp_tx.h" + #include "debug.h" ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + #include "debugfs_sta.h" ++#endif + #include "hw.h" + #include "peer.h" + #include "mac.h" +@@ -550,7 +552,9 @@ static void ath11k_dp_tx_cache_peer_stat + void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts) + { + struct ath11k_base *ab = ar->ab; ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats; ++#endif + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum hal_tx_rate_stats_bw bw; +@@ -639,8 +643,10 @@ void ath11k_dp_tx_update_txcompl(struct + ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); + } + ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) + ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx); ++#endif + + err_out: + spin_unlock_bh(&ab->base_lock); +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -9811,7 +9811,7 @@ static const struct ieee80211_ops ath11k + .set_wakeup = ath11k_wow_op_set_wakeup, + #endif + +-#ifdef CPTCFG_ATH11K_DEBUGFS ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + .sta_add_debugfs = ath11k_debugfs_sta_op_add, + #endif + +--- a/local-symbols ++++ b/local-symbols +@@ -176,6 +176,8 @@ ATH11K_MEM_PROFILE_256M= + ATH11K_MEM_PROFILE_512M= + ATH11K_DEBUG= + ATH11K_DEBUGFS= ++ATH11K_DEBUGFS_STA= ++ATH11K_DEBUGFS_HTT_STATS= + ATH11K_TRACING= + ATH11K_SPECTRAL= + ATH11K_THERMAL= diff --git a/package/kernel/mac80211/patches/nss/ath11k/909-wifi-ath11k-fix-invalid-access-to-memory.patch b/package/kernel/mac80211/patches/nss/ath11k/909-wifi-ath11k-fix-invalid-access-to-memory.patch new file mode 100644 index 00000000000000..da069c0de63163 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/909-wifi-ath11k-fix-invalid-access-to-memory.patch @@ -0,0 +1,47 @@ +From cea94c73e068ce4e015327bf251f782545d8e365 Mon Sep 17 00:00:00 2001 +From: Sarika Sharma +Date: Mon, 29 Jan 2024 16:01:23 +0530 +Subject: [PATCH] wifi: ath11k: fix invalid access to memory + +In ath11k_dp_rx_msdu_coalesce(), rxcb is fetched from skb and bool +is_continuation is part of rxcb. +Currently, after freeing the skb, the rxcb->is_continuation accessed +again which is wrong since the memory is already freed. + +Hence fix the issue by locally defining bool is_continuation from rxcb, +so that after freeing skb also we can use is_continuation. + +Signed-off-by: Sarika Sharma +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2202,6 +2202,7 @@ static int ath11k_dp_rx_msdu_coalesce(st + struct hal_rx_desc *ldesc; + int space_extra, rem_len, buf_len; + u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; ++ bool is_continuation; + + /* As the msdu is spread across multiple rx buffers, + * find the offset to the start of msdu for computing +@@ -2250,7 +2251,8 @@ static int ath11k_dp_rx_msdu_coalesce(st + rem_len = msdu_len - buf_first_len; + while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { + rxcb = ATH11K_SKB_RXCB(skb); +- if (rxcb->is_continuation) ++ is_continuation = rxcb->is_continuation; ++ if (is_continuation) + buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz; + else + buf_len = rem_len; +@@ -2268,7 +2270,7 @@ static int ath11k_dp_rx_msdu_coalesce(st + dev_kfree_skb_any(skb); + + rem_len -= buf_len; +- if (!rxcb->is_continuation) ++ if (!is_continuation) + break; + } + diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-233-ath11k-Disable-rx_header-tlv-for-2K-SKB.patch b/package/kernel/mac80211/patches/nss/ath11k/999-233-ath11k-Disable-rx_header-tlv-for-2K-SKB.patch new file mode 100644 index 00000000000000..322698f0ea8d84 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-233-ath11k-Disable-rx_header-tlv-for-2K-SKB.patch @@ -0,0 +1,1010 @@ +From 30f54666ae15128f26fbad787a35253885a10513 Mon Sep 17 00:00:00 2001 +From: Ramya Gnanasekar +Date: Fri, 25 Dec 2020 16:11:06 +0530 +Subject: [PATCH] ath11k: Disable rx_header tlv for 2K SKB + +On low memory platform hdr_status in hal_rx_desc is not subscribed to +get a savings of 128bytes in skb. This is required to reduce the skb +size from 4K to 2K. Use HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG message +to unsubscribe rx_pkt_header tlv for rxdma ring. + +Signed-off-by: Ramya Gnanasekar + +--- a/drivers/net/wireless/ath/ath11k/debugfs.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs.c +@@ -668,6 +668,7 @@ static ssize_t ath11k_write_extd_rx_stat + } + + ar->debug.rx_filter = tlv_filter.rx_filter; ++ tlv_filter.offset_valid = false; + + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; +@@ -1228,6 +1229,7 @@ static ssize_t ath11k_write_pktlog_filte + } + + /* Clear rx filter set for monitor mode and rx status */ ++ tlv_filter.offset_valid = false; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -237,7 +237,8 @@ struct ath11k_pdev_dp { + #define DP_REO_CMD_RING_SIZE 256 + #define DP_REO_STATUS_RING_SIZE 2048 + #define DP_RXDMA_BUF_RING_SIZE 4096 +-#define DP_RXDMA_REFILL_RING_SIZE 2048 ++#define DP_RXDMA_REFILL_RING_SIZE ATH11K_DP_RXDMA_REFILL_RING_SIZE ++#define DP_RXDMA_NSS_REFILL_RING_SIZE ATH11K_DP_RXDMA_NSS_REFILL_RING_SIZE + #define DP_RXDMA_ERR_DST_RING_SIZE 1024 + #define DP_RXDMA_MON_STATUS_RING_SIZE ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE + #define DP_RXDMA_MONITOR_BUF_RING_SIZE ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE +@@ -672,7 +673,7 @@ enum htt_stats_internal_ppdu_frametype { + * + * |31 26|25|24|23 16|15 8|7 0| + * |-----------------+----------------+----------------+---------------| +- * | rsvd1 |PS|SS| ring_id | pdev_id | msg_type | ++ * | rsvd1|OV|PS|SS| ring_id | pdev_id | msg_type | + * |-------------------------------------------------------------------| + * | rsvd2 | ring_buffer_size | + * |-------------------------------------------------------------------| +@@ -686,6 +687,14 @@ enum htt_stats_internal_ppdu_frametype { + * |-------------------------------------------------------------------| + * | tlv_filter_in_flags | + * |-------------------------------------------------------------------| ++ * | rx_header_offset | rx_packet_offset | ++ * |-------------------------------------------------------------------| ++ * | rx_mpdu_start_offset | rx_mpdu_end_offset | ++ * |-------------------------------------------------------------------| ++ * | rx_msdu_start_offset | rx_msdu_end_offset | ++ * |-------------------------------------------------------------------| ++ * | rsvd3 | rx_attention_offset | ++ * |-------------------------------------------------------------------| + * Where: + * PS = pkt_swap + * SS = status_swap +@@ -699,7 +708,10 @@ enum htt_stats_internal_ppdu_frametype { + * More details can be got from enum htt_srng_ring_id + * b'24 - status_swap: 1 is to swap status TLV + * b'25 - pkt_swap: 1 is to swap packet TLV +- * b'26:31 - rsvd1: reserved for future use ++ * b'26 - rx_offset_valid (OV): flag to indicate rx offsets ++ * configuration fields are valid ++ * ++ * b'27:31 - rsvd1: reserved for future use + * dword1 - b'0:16 - ring_buffer_size: size of buffers referenced by rx ring, + * in byte units. + * Valid only for HW_TO_SW_RING and SW_TO_HW_RING +@@ -728,6 +740,42 @@ enum htt_stats_internal_ppdu_frametype { + * dword6 - b'0:31 - tlv_filter_in_flags: + * Filter in Attention/MPDU/PPDU/Header/User tlvs + * Refer to CFG_TLV_FILTER_IN_FLAG defs ++ * dword7 - b'0:15 - rx_packet_offset: rx_packet_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_1 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * - b'16:31 - rx_header_offset: rx_header_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_1 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * dword8 - b'0:15 - rx_mpdu_end_offset: rx_mpdu_end_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_2 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * - b'16:31 - rx_mpdu_start_offset: rx_mpdu_start_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_2 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * dword9 - b'0:15 - rx_msdu_end_offset: rx_msdu_end_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_3 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * - b'16:31 - rx_msdu_start_offset: rx_msdu_start_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_3 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * dword10- b'0:15 - rx_attention_offset: rx_attention_offset in byte units ++ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING ++ * A value of 0 will be considered as ignore this config. ++ * Refer to BUF_RING_CFG_4 defs within HW .h files, ++ * e.g. wmac_top_reg_seq_hwioreg.h ++ * - b'16:31 - rsvd3 for future use + */ + + #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +@@ -735,8 +783,16 @@ enum htt_stats_internal_ppdu_frametype { + #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) + #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) + #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) ++#define HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID BIT(26) + + #define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) ++#define HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET GENMASK(15, 0) ++#define HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET GENMASK(31, 16) ++#define HTT_RX_RING_SELECTION_CFG_RX_MPDU_END_OFFSET GENMASK(15, 0) ++#define HTT_RX_RING_SELECTION_CFG_RX_MPDU_START_OFFSET GENMASK(31, 16) ++#define HTT_RX_RING_SELECTION_CFG_RX_MSDU_END_OFFSET GENMASK(15, 0) ++#define HTT_RX_RING_SELECTION_CFG_RX_MSDU_START_OFFSET GENMASK(31, 16) ++#define HTT_RX_RING_SELECTION_CFG_RX_ATTENTION_OFFSET GENMASK(15, 0) + + enum htt_rx_filter_tlv_flags { + HTT_RX_FILTER_TLV_FLAGS_MPDU_START = BIT(0), +@@ -1040,6 +1096,14 @@ enum htt_rx_data_pkt_filter_tlv_flasg3 { + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_ATTENTION) + ++#define HTT_RX_RXDMA_FILTER_TLV_FLAGS_BUF_RING \ ++ (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ ++ HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ ++ HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ ++ HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ ++ HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ ++ HTT_RX_FILTER_TLV_FLAGS_ATTENTION) ++ + struct htt_rx_ring_selection_cfg_cmd { + u32 info0; + u32 info1; +@@ -1048,6 +1112,10 @@ struct htt_rx_ring_selection_cfg_cmd { + u32 pkt_type_en_flags2; + u32 pkt_type_en_flags3; + u32 rx_filter_tlv; ++ u32 rx_packet_offset; ++ u32 rx_mpdu_offset; ++ u32 rx_msdu_offset; ++ u32 rx_attn_offset; + } __packed; + + struct htt_rx_ring_tlv_filter { +@@ -1056,6 +1124,14 @@ struct htt_rx_ring_tlv_filter { + u32 pkt_filter_flags1; /* MGMT */ + u32 pkt_filter_flags2; /* CTRL */ + u32 pkt_filter_flags3; /* DATA */ ++ bool offset_valid; ++ u16 rx_packet_offset; ++ u16 rx_header_offset; ++ u16 rx_mpdu_end_offset; ++ u16 rx_mpdu_start_offset; ++ u16 rx_msdu_end_offset; ++ u16 rx_msdu_start_offset; ++ u16 rx_attn_offset; + }; + + #define HTT_RX_FULL_MON_MODE_CFG_CMD_INFO0_MSG_TYPE GENMASK(7, 0) +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -73,6 +73,12 @@ static inline bool ath11k_dp_rx_h_mpdu_s + return ab->hw_params.hw_ops->rx_desc_get_mpdu_fc_valid(desc); + } + ++static u16 ath11k_dp_rxdesc_get_mpdu_frame_ctrl(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_get_mpdu_frame_ctl(desc); ++} ++ + static inline bool ath11k_dp_rx_h_mpdu_start_more_frags(struct ath11k_base *ab, + struct sk_buff *skb) + { +@@ -309,6 +315,35 @@ static u8 *ath11k_dp_rxdesc_mpdu_start_a + return ab->hw_params.hw_ops->rx_desc_mpdu_start_addr2(desc); + } + ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++static void ath11k_dp_get_rx_header_offset(struct ath11k_base *ab, ++ struct htt_rx_ring_tlv_filter *tlv_filter) ++{ ++ ab->hw_params.hw_ops->rx_desc_get_offset(tlv_filter); ++} ++#endif ++ ++static bool ath11k_dp_rx_desc_dot11_hdr_fields_valid(struct ath11k_base *ab, ++ struct hal_rx_desc *desc) ++{ ++ return ab->hw_params.hw_ops->rx_desc_dot11_hdr_fields_valid(desc); ++} ++ ++static void ath11k_dp_rx_desc_get_dot11_hdr(struct ath11k_base *ab, ++ struct hal_rx_desc *desc, ++ struct ieee80211_hdr *hdr) ++{ ++ ab->hw_params.hw_ops->rx_desc_get_dot11_hdr(desc, hdr); ++} ++ ++static void ath11k_dp_rx_desc_get_crypto_header(struct ath11k_base *ab, ++ struct hal_rx_desc *desc, ++ u8 *crypto_hdr, ++ enum hal_encrypt_type enctype) ++{ ++ ab->hw_params.hw_ops->rx_desc_get_crypto_header(desc, crypto_hdr, enctype); ++} ++ + static void ath11k_dp_service_mon_ring(struct timer_list *t) + { + struct ath11k_base *ab = from_timer(ab, t, mon_reap_timer); +@@ -2389,6 +2424,49 @@ int ath11k_dp_rx_crypto_icv_len(struct a + return 0; + } + ++static void ath11k_get_dot11_hdr_from_rx_desc(struct ath11k *ar, ++ struct sk_buff *msdu, ++ struct ath11k_skb_rxcb *rxcb, ++ struct ieee80211_rx_status *status, ++ enum hal_encrypt_type enctype) ++{ ++ struct hal_rx_desc *rx_desc = rxcb->rx_desc; ++ struct ath11k_base *ab = ar->ab; ++ size_t hdr_len, crypto_len; ++ struct ieee80211_hdr *hdr; ++ u16 fc, qos_ctl = 0; ++ u8 *crypto_hdr; ++ ++ if (!(status->flag & RX_FLAG_IV_STRIPPED)) { ++ crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); ++ crypto_hdr = skb_push(msdu, crypto_len); ++ ath11k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype); ++ } ++ ++ fc = ath11k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc); ++ hdr_len = ieee80211_hdrlen(fc); ++ skb_push(msdu, hdr_len); ++ hdr = (struct ieee80211_hdr *)msdu->data; ++ hdr->frame_control = fc; ++ ++ /* Get wifi header from rx_desc */ ++ ath11k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, hdr); ++ ++ if (rxcb->is_mcbc) ++ status->flag &= ~RX_FLAG_PN_VALIDATED; ++ ++ /* Add QOS header */ ++ if (ieee80211_is_data_qos(hdr->frame_control)) { ++ qos_ctl = rxcb->tid; ++ if (ath11k_dp_rx_h_msdu_start_mesh_ctl_present(ab, rx_desc)) ++ qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT; ++ ++ /* TODO Add other QoS ctl fields when required */ ++ memcpy(msdu->data + (hdr_len - IEEE80211_QOS_CTL_LEN), ++ &qos_ctl, IEEE80211_QOS_CTL_LEN); ++ } ++} ++ + static void ath11k_dp_rx_h_undecap_nwifi(struct ath11k *ar, + struct sk_buff *msdu, + u8 *first_hdr, +@@ -2402,7 +2480,8 @@ static void ath11k_dp_rx_h_undecap_nwifi + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u16 qos_ctl = 0; +- u8 *qos; ++ u8 *qos, *crypto_hdr; ++ bool add_qos_ctrl = false; + + /* copy SA & DA and pull decapped header */ + hdr = (struct ieee80211_hdr *)msdu->data; +@@ -2411,7 +2490,7 @@ static void ath11k_dp_rx_h_undecap_nwifi + ether_addr_copy(sa, ieee80211_get_SA(hdr)); + skb_pull(msdu, ieee80211_hdrlen(hdr->frame_control)); + +- if (rxcb->is_first_msdu) { ++ if (rxcb->is_first_msdu && first_hdr) { + /* original 802.11 header is valid for the first msdu + * hence we can reuse the same header + */ +@@ -2441,16 +2520,23 @@ static void ath11k_dp_rx_h_undecap_nwifi + + /* copy decap header before overwriting for reuse below */ + memcpy(decap_hdr, (uint8_t *)hdr, hdr_len); ++ add_qos_ctrl = true; + } + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { +- memcpy(skb_push(msdu, +- ath11k_dp_rx_crypto_param_len(ar, enctype)), +- (void *)hdr + hdr_len, +- ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ if (first_hdr) { ++ memcpy(skb_push(msdu, ++ ath11k_dp_rx_crypto_param_len(ar, enctype)), ++ (void *)hdr + hdr_len, ++ ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ } else { ++ crypto_hdr = skb_push(msdu, ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ ath11k_dp_rx_desc_get_crypto_header(ar->ab, ++ rxcb->rx_desc, crypto_hdr, enctype); ++ } + } + +- if (!rxcb->is_first_msdu) { ++ if (!rxcb->is_first_msdu || add_qos_ctrl) { + memcpy(skb_push(msdu, + IEEE80211_QOS_CTL_LEN), &qos_ctl, + IEEE80211_QOS_CTL_LEN); +@@ -2566,6 +2652,20 @@ static void ath11k_dp_rx_h_undecap_eth(s + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + void *rfc1042; ++ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); ++ struct ath11k_dp_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}}; ++ ++ if (!first_hdr) { ++ eth = (struct ethhdr *)msdu->data; ++ ether_addr_copy(da, eth->h_dest); ++ ether_addr_copy(sa, eth->h_source); ++ rfc.snap_type = eth->h_proto; ++ skb_pull(msdu, sizeof(struct ethhdr)); ++ memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), &rfc, ++ sizeof(struct ath11k_dp_rfc1042_hdr)); ++ ath11k_get_dot11_hdr_from_rx_desc(ar, msdu, rxcb, status, enctype); ++ goto exit; ++ } + + rfc1042 = ath11k_dp_rx_h_find_rfc1042(ar, msdu, enctype); + if (WARN_ON_ONCE(!rfc1042)) +@@ -2594,6 +2694,7 @@ static void ath11k_dp_rx_h_undecap_eth(s + + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + ++exit: + /* original 802.11 header has a different DA and in + * case of 4addr it may also have different SA + */ +@@ -2612,6 +2713,7 @@ static void ath11k_dp_rx_h_undecap_snap( + size_t hdr_len; + u8 l3_pad_bytes; + struct hal_rx_desc *rx_desc; ++ struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + + /* Delivered decapped frame: + * [amsdu header] <-- replaced with 802.11 hdr +@@ -2625,6 +2727,11 @@ static void ath11k_dp_rx_h_undecap_snap( + skb_put(msdu, l3_pad_bytes); + skb_pull(msdu, sizeof(struct ath11k_dp_amsdu_subframe_hdr) + l3_pad_bytes); + ++ if (!first_hdr) { ++ ath11k_get_dot11_hdr_from_rx_desc(ar, msdu, rxcb, status, enctype); ++ return; ++ } ++ + hdr = (struct ieee80211_hdr *)first_hdr; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + +@@ -3098,6 +3205,20 @@ static int ath11k_dp_rx_process_msdu(str + goto free_out; + } + ++ hdr_status = ath11k_dp_rx_h_80211_hdr(ab, rx_desc); ++ /* wifi hdr fields validation for 512M:: ++ * Mcast packets in ethernet frame mode ++ * will need wifi hdr in msdu to validate PN. ++ * Header will be added in undecap routine. ++ * Validation on wifi hdr fields from rx_desc. ++ */ ++ if (!hdr_status && ath11k_dp_rx_h_attn_is_mcbc(ab, rx_desc) && ++ !ath11k_dp_rx_desc_dot11_hdr_fields_valid(ab, rx_desc)) { ++ ath11k_warn(ab, "One or more invalid dot11 header fields\n"); ++ ret = -EIO; ++ goto free_out; ++ } ++ + rxcb = ATH11K_SKB_RXCB(msdu); + rxcb->rx_desc = rx_desc; + msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ab, rx_desc); +@@ -3110,8 +3231,9 @@ static int ath11k_dp_rx_process_msdu(str + hdr_status = ath11k_dp_rx_h_80211_hdr(ab, rx_desc); + ret = -EINVAL; + ath11k_warn(ab, "invalid msdu len %u\n", msdu_len); +- ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", hdr_status, +- sizeof(struct ieee80211_hdr)); ++ if (hdr_status) ++ ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", hdr_status, ++ sizeof(struct ieee80211_hdr)); + ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", rx_desc, + sizeof(struct hal_rx_desc)); + goto free_out; +@@ -4070,6 +4192,7 @@ static int ath11k_dp_rx_h_verify_tkip_mi + + hdr = (struct ieee80211_hdr *)(msdu->data + hal_rx_desc_sz); + hdr_len = ieee80211_hdrlen(hdr->frame_control); ++ + head_len = hdr_len + hal_rx_desc_sz + IEEE80211_TKIP_IV_LEN; + tail_len = IEEE80211_CCMP_MIC_LEN + IEEE80211_TKIP_ICV_LEN + FCS_LEN; + +@@ -4350,8 +4473,8 @@ static void ath11k_dp_rx_h_sort_frags(st + + static u64 ath11k_dp_rx_h_get_pn(struct ath11k *ar, struct sk_buff *skb) + { +- struct ieee80211_hdr *hdr; + u64 pn = 0; ++ struct ieee80211_hdr *hdr; + u8 *ehdr; + u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; + +@@ -4581,8 +4704,9 @@ ath11k_dp_process_rx_err_buf(struct ath1 + if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) { + hdr_status = ath11k_dp_rx_h_80211_hdr(ar->ab, rx_desc); + ath11k_warn(ar->ab, "invalid msdu leng %u", msdu_len); +- ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "", hdr_status, +- sizeof(struct ieee80211_hdr)); ++ if (hdr_status) ++ ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "", hdr_status, ++ sizeof(struct ieee80211_hdr)); + ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "", rx_desc, + sizeof(struct hal_rx_desc)); + dev_kfree_skb_any(msdu); +@@ -5207,6 +5331,47 @@ void ath11k_dp_rx_pdev_free(struct ath11 + ath11k_dp_rxdma_pdev_buf_free(ar); + } + ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++static int ath11k_dp_rxdma_ring_sel_config(struct ath11k *ar) ++{ ++ struct ath11k_pdev_dp *dp = &ar->dp; ++ struct htt_rx_ring_tlv_filter tlv_filter = {0}; ++ u32 ring_id; ++ int ret; ++ u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; ++ ++ ring_id = dp->rx_refill_buf_ring.refill_buf_ring.ring_id; ++ ++ tlv_filter.rx_filter = HTT_RX_RXDMA_FILTER_TLV_FLAGS_BUF_RING; ++ tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_PKT_FILTER_TLV_FLAGS2_BAR; ++ tlv_filter.pkt_filter_flags3 = HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_MCAST | ++ HTT_RX_FP_DATA_PKT_FILTER_TLV_FLASG3_UCAST; ++ tlv_filter.offset_valid = true; ++ tlv_filter.rx_packet_offset = hal_rx_desc_sz; ++ tlv_filter.rx_header_offset = 0; ++ ++ ath11k_dp_get_rx_header_offset(ar->ab, &tlv_filter); ++ ++ if (!ar->ab->nss.enabled) ++ ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, ++ HAL_RXDMA_BUF, ++ DP_RXDMA_REFILL_RING_SIZE, ++ &tlv_filter); ++ else ++ ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, ++ HAL_RXDMA_BUF, ++ DP_RXDMA_NSS_REFILL_RING_SIZE, ++ &tlv_filter); ++ ++ return ret; ++} ++#else ++static int ath11k_dp_rxdma_ring_sel_config(struct ath11k *ar) ++{ ++ return 0; ++} ++#endif ++ + int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) + { + struct ath11k *ar = ab->pdevs[mac_id].ar; +@@ -5300,6 +5465,12 @@ config_refill_ring: + } + } + ++ ret = ath11k_dp_rxdma_ring_sel_config(ar); ++ if (ret) { ++ ath11k_warn(ab, "failed to setup rxdma ring selection config\n"); ++ return ret; ++ } ++ + return 0; + } + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -1263,6 +1263,8 @@ int ath11k_dp_tx_htt_rx_filter_setup(str + !!(params.flags & HAL_SRNG_FLAGS_MSI_SWAP)); + cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS, + !!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP)); ++ cmd->info0 |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID, ++ tlv_filter->offset_valid); + + cmd->info1 = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE, + rx_buf_size); +@@ -1272,6 +1274,26 @@ int ath11k_dp_tx_htt_rx_filter_setup(str + cmd->pkt_type_en_flags3 = tlv_filter->pkt_filter_flags3; + cmd->rx_filter_tlv = tlv_filter->rx_filter; + ++ if (tlv_filter->offset_valid) { ++ cmd->rx_packet_offset = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET, ++ tlv_filter->rx_packet_offset); ++ cmd->rx_packet_offset |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET, ++ tlv_filter->rx_header_offset); ++ ++ cmd->rx_mpdu_offset = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_MPDU_END_OFFSET, ++ tlv_filter->rx_mpdu_end_offset); ++ cmd->rx_mpdu_offset |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_MPDU_START_OFFSET, ++ tlv_filter->rx_mpdu_start_offset); ++ ++ cmd->rx_msdu_offset = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_MSDU_END_OFFSET, ++ tlv_filter->rx_msdu_end_offset); ++ cmd->rx_msdu_offset |= FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_MSDU_START_OFFSET, ++ tlv_filter->rx_msdu_start_offset); ++ ++ cmd->rx_attn_offset = FIELD_PREP(HTT_RX_RING_SELECTION_CFG_RX_ATTENTION_OFFSET, ++ tlv_filter->rx_attn_offset); ++ } ++ + ret = ath11k_htc_send(&ab->htc, ab->dp.eid, skb); + if (ret) + goto err_free; +@@ -1350,6 +1372,7 @@ int ath11k_dp_tx_htt_monitor_mode_ring_c + } + + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; ++ tlv_filter.offset_valid = false; + + if (!reset) { + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -260,7 +260,11 @@ static u8 ath11k_hw_ipq8074_rx_desc_get_ + + static u8 *ath11k_hw_ipq8074_rx_desc_get_hdr_status(struct hal_rx_desc *desc) + { ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + return desc->u.ipq8074.hdr_status; ++#else ++ return NULL; ++#endif + } + + static bool ath11k_hw_ipq8074_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +@@ -405,26 +409,132 @@ static void ath11k_hw_ipq8074_rx_desc_se + desc->u.ipq8074.msdu_start.info1 = __cpu_to_le32(info); + } + ++static ++struct rx_attention *ath11k_hw_ipq8074_rx_desc_get_attention(struct hal_rx_desc *desc) ++{ ++ return &desc->u.ipq8074.attention; ++} ++ ++static u8 *ath11k_hw_ipq8074_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) ++{ ++ return &desc->u.ipq8074.msdu_payload[0]; ++} ++ ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++static void ath11k_hw_ipq8074_rx_desc_get_offset(struct htt_rx_ring_tlv_filter *tlv_filter) ++{ ++ tlv_filter->rx_mpdu_end_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_ipq8074, mpdu_end_tag)); ++ tlv_filter->rx_mpdu_start_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_ipq8074, mpdu_start_tag)); ++ tlv_filter->rx_msdu_end_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_ipq8074, msdu_end_tag)); ++ tlv_filter->rx_msdu_start_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_ipq8074, msdu_start_tag)); ++ tlv_filter->rx_attn_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_ipq8074, rx_attn_tag)); ++} ++#endif ++ ++static u16 ath11k_hw_ipq8074_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) ++{ ++ return __le16_to_cpu(desc->u.ipq8074.mpdu_start.frame_ctrl); ++} ++ + static bool ath11k_hw_ipq8074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) + { + return __le32_to_cpu(desc->u.ipq8074.mpdu_start.info1) & + RX_MPDU_START_INFO1_MAC_ADDR2_VALID; + } + +-static u8 *ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) ++static u8* ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) + { + return desc->u.ipq8074.mpdu_start.addr2; + } + +-static +-struct rx_attention *ath11k_hw_ipq8074_rx_desc_get_attention(struct hal_rx_desc *desc) ++static bool ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid(struct hal_rx_desc *desc) + { +- return &desc->u.ipq8074.attention; ++ if ((ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld(desc) && ++ ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid(desc) && ++ __le32_to_cpu(desc->u.ipq8074.mpdu_start.info1) & ++ RX_MPDU_START_INFO1_MAC_ADDR1_VALID && ++ ath11k_hw_ipq8074_rx_desc_mac_addr2_valid(desc) && ++ __le32_to_cpu(desc->u.ipq8074.mpdu_start.info1) & ++ RX_MPDU_START_INFO1_MAC_ADDR3_VALID && ++ FIELD_GET((RX_MPDU_START_INFO1_MPDU_DUR_VALID), ++ __le32_to_cpu(desc->u.ipq8074.mpdu_start.info1)))) { ++ return true; ++ } ++ return false; ++} ++ ++static void ath11k_hw_ipq8074_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, ++ struct ieee80211_hdr *hdr) ++{ ++ hdr->frame_control = __le16_to_cpu(desc->u.ipq8074.mpdu_start.frame_ctrl); ++ hdr->duration_id = __le16_to_cpu(desc->u.ipq8074.mpdu_start.duration); ++ ether_addr_copy(hdr->addr1, desc->u.ipq8074.mpdu_start.addr1); ++ ether_addr_copy(hdr->addr2, desc->u.ipq8074.mpdu_start.addr2); ++ ether_addr_copy(hdr->addr3, desc->u.ipq8074.mpdu_start.addr3); ++ if (__le32_to_cpu(desc->u.ipq8074.mpdu_start.info1) & ++ RX_MPDU_START_INFO1_MAC_ADDR4_VALID) { ++ ether_addr_copy(hdr->addr4, desc->u.ipq8074.mpdu_start.addr4); ++ } ++ hdr->seq_ctrl = __le16_to_cpu(desc->u.ipq8074.mpdu_start.seq_ctrl); ++} ++ ++static void ath11k_hw_ipq8074_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, ++ u8 *crypto_hdr, ++ enum hal_encrypt_type enctype) ++{ ++ unsigned int key_id; ++ ++ switch (enctype) { ++ case HAL_ENCRYPT_TYPE_OPEN: ++ return; ++ case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: ++ case HAL_ENCRYPT_TYPE_TKIP_MIC: ++ crypto_hdr[0] = ++ HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.ipq8074.mpdu_start.pn[0]); ++ crypto_hdr[1] = 0; ++ crypto_hdr[2] = ++ HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.ipq8074.mpdu_start.pn[0]); ++ break; ++ case HAL_ENCRYPT_TYPE_CCMP_128: ++ case HAL_ENCRYPT_TYPE_CCMP_256: ++ case HAL_ENCRYPT_TYPE_GCMP_128: ++ case HAL_ENCRYPT_TYPE_AES_GCMP_256: ++ crypto_hdr[0] = ++ HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.ipq8074.mpdu_start.pn[0]); ++ crypto_hdr[1] = ++ HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.ipq8074.mpdu_start.pn[0]); ++ crypto_hdr[2] = 0; ++ break; ++ case HAL_ENCRYPT_TYPE_WEP_40: ++ case HAL_ENCRYPT_TYPE_WEP_104: ++ case HAL_ENCRYPT_TYPE_WEP_128: ++ case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: ++ case HAL_ENCRYPT_TYPE_WAPI: ++ return; ++ } ++ key_id = FIELD_GET(RX_MPDU_START_INFO5_KEY_ID, ++ __le32_to_cpu(desc->u.ipq8074.mpdu_start.info5)); ++ crypto_hdr[3] = 0x20 | (key_id << 6); ++ crypto_hdr[4] = HAL_RX_MPDU_INFO_PN_GET_BYTE3(desc->u.ipq8074.mpdu_start.pn[0]); ++ crypto_hdr[5] = HAL_RX_MPDU_INFO_PN_GET_BYTE4(desc->u.ipq8074.mpdu_start.pn[0]); ++ crypto_hdr[6] = HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.ipq8074.mpdu_start.pn[1]); ++ crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.ipq8074.mpdu_start.pn[1]); + } + +-static u8 *ath11k_hw_ipq8074_rx_desc_get_msdu_payload(struct hal_rx_desc *desc) ++static bool ath11k_hw_qcn9074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) + { +- return &desc->u.ipq8074.msdu_payload[0]; ++ return __le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & ++ RX_MPDU_START_INFO11_MAC_ADDR2_VALID; ++} ++ ++static u8* ath11k_hw_qcn9074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) ++{ ++ return desc->u.qcn9074.mpdu_start.addr2; + } + + static bool ath11k_hw_qcn9074_rx_desc_get_first_msdu(struct hal_rx_desc *desc) +@@ -447,7 +557,11 @@ static u8 ath11k_hw_qcn9074_rx_desc_get_ + + static u8 *ath11k_hw_qcn9074_rx_desc_get_hdr_status(struct hal_rx_desc *desc) + { ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + return desc->u.qcn9074.hdr_status; ++#else ++ return NULL; ++#endif + } + + static bool ath11k_hw_qcn9074_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +@@ -634,7 +748,11 @@ static u8 ath11k_hw_wcn6855_rx_desc_get_ + + static u8 *ath11k_hw_wcn6855_rx_desc_get_hdr_status(struct hal_rx_desc *desc) + { ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + return desc->u.wcn6855.hdr_status; ++#else ++ return NULL; ++#endif + } + + static bool ath11k_hw_wcn6855_rx_desc_encrypt_valid(struct hal_rx_desc *desc) +@@ -784,6 +902,96 @@ static u8 *ath11k_hw_wcn6855_rx_desc_mpd + { + return desc->u.wcn6855.mpdu_start.addr2; + } ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++static void ath11k_hw_qcn9074_rx_desc_get_offset(struct htt_rx_ring_tlv_filter *tlv_filter) ++{ ++ tlv_filter->rx_mpdu_end_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_qcn9074, mpdu_end_tag)); ++ tlv_filter->rx_mpdu_start_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_qcn9074, mpdu_start_tag)); ++ tlv_filter->rx_msdu_end_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_qcn9074, msdu_end_tag)); ++ tlv_filter->rx_msdu_start_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_qcn9074, msdu_start_tag)); ++ tlv_filter->rx_attn_offset = __le16_to_cpu(offsetof ++ (struct hal_rx_desc_qcn9074, rx_attn_tag)); ++} ++#endif ++ ++static u16 ath11k_hw_qcn9074_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) ++{ ++ return __le16_to_cpu(desc->u.qcn9074.mpdu_start.frame_ctrl); ++} ++ ++static bool ath11k_hw_qcn9074_rx_desc_dot11_hdr_fields_valid(struct hal_rx_desc *desc) ++{ ++ if ((ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld(desc) && ++ ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid(desc) && ++ (__le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & ++ RX_MPDU_START_INFO11_MAC_ADDR1_VALID) && ++ ath11k_hw_qcn9074_rx_desc_mac_addr2_valid(desc) && ++ (__le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & ++ RX_MPDU_START_INFO11_MAC_ADDR3_VALID) && ++ FIELD_GET((RX_MPDU_START_INFO11_MPDU_DUR_VALID), ++ __le32_to_cpu(desc->u.qcn9074.mpdu_start.info11)))) { ++ return true; ++ } ++ return false; ++} ++ ++static void ath11k_hw_qcn9074_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, ++ struct ieee80211_hdr *hdr) ++{ ++ hdr->frame_control = __le16_to_cpu(desc->u.qcn9074.mpdu_start.frame_ctrl); ++ hdr->duration_id = __le16_to_cpu(desc->u.qcn9074.mpdu_start.duration); ++ ether_addr_copy(hdr->addr1, desc->u.qcn9074.mpdu_start.addr1); ++ ether_addr_copy(hdr->addr2, desc->u.qcn9074.mpdu_start.addr2); ++ ether_addr_copy(hdr->addr3, desc->u.qcn9074.mpdu_start.addr3); ++ if (__le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & ++ RX_MPDU_START_INFO11_MAC_ADDR4_VALID) { ++ ether_addr_copy(hdr->addr4, desc->u.qcn9074.mpdu_start.addr4); ++ } ++ hdr->seq_ctrl = __le16_to_cpu(desc->u.qcn9074.mpdu_start.seq_ctrl); ++} ++ ++static void ath11k_hw_qcn9074_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, ++ u8 *crypto_hdr, ++ enum hal_encrypt_type enctype) ++{ ++ unsigned int key_id; ++ ++ switch (enctype) { ++ case HAL_ENCRYPT_TYPE_OPEN: ++ return; ++ case HAL_ENCRYPT_TYPE_TKIP_NO_MIC: ++ case HAL_ENCRYPT_TYPE_TKIP_MIC: ++ crypto_hdr[0] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9074.mpdu_start.pn[0]); ++ crypto_hdr[1] = 0; ++ crypto_hdr[2] = HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcn9074.mpdu_start.pn[0]); ++ break; ++ case HAL_ENCRYPT_TYPE_CCMP_128: ++ case HAL_ENCRYPT_TYPE_CCMP_256: ++ case HAL_ENCRYPT_TYPE_GCMP_128: ++ case HAL_ENCRYPT_TYPE_AES_GCMP_256: ++ crypto_hdr[0] = HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcn9074.mpdu_start.pn[0]); ++ crypto_hdr[1] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9074.mpdu_start.pn[0]); ++ crypto_hdr[2] = 0; ++ break; ++ case HAL_ENCRYPT_TYPE_WEP_40: ++ case HAL_ENCRYPT_TYPE_WEP_104: ++ case HAL_ENCRYPT_TYPE_WEP_128: ++ case HAL_ENCRYPT_TYPE_WAPI_GCM_SM4: ++ case HAL_ENCRYPT_TYPE_WAPI: ++ return; ++ } ++ key_id = FIELD_GET(RX_MPDU_START_INFO12_KEY_ID, ++ __le32_to_cpu(desc->u.qcn9074.mpdu_start.info12)); ++ crypto_hdr[3] = 0x20 | (key_id << 6); ++ crypto_hdr[4] = HAL_RX_MPDU_INFO_PN_GET_BYTE3(desc->u.qcn9074.mpdu_start.pn[0]); ++ crypto_hdr[5] = HAL_RX_MPDU_INFO_PN_GET_BYTE4(desc->u.qcn9074.mpdu_start.pn[0]); ++ crypto_hdr[6] = HAL_RX_MPDU_INFO_PN_GET_BYTE1(desc->u.qcn9074.mpdu_start.pn[1]); ++ crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9074.mpdu_start.pn[1]); ++} + + static void ath11k_hw_wcn6855_reo_setup(struct ath11k_base *ab) + { +@@ -988,6 +1196,13 @@ const struct ath11k_hw_ops ipq8074_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ .rx_desc_get_offset = ath11k_hw_ipq8074_rx_desc_get_offset, ++#endif ++ .rx_desc_get_mpdu_frame_ctl = ath11k_hw_ipq8074_rx_desc_get_mpdu_frame_ctl, ++ .rx_desc_dot11_hdr_fields_valid = ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid, ++ .rx_desc_get_dot11_hdr = ath11k_hw_ipq8074_rx_desc_get_dot11_hdr, ++ .rx_desc_get_crypto_header = ath11k_hw_ipq8074_rx_desc_get_crypto_hdr, + }; + + const struct ath11k_hw_ops ipq6018_ops = { +@@ -1029,6 +1244,13 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ .rx_desc_get_offset = ath11k_hw_ipq8074_rx_desc_get_offset, ++#endif ++ .rx_desc_get_mpdu_frame_ctl = ath11k_hw_ipq8074_rx_desc_get_mpdu_frame_ctl, ++ .rx_desc_dot11_hdr_fields_valid = ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid, ++ .rx_desc_get_dot11_hdr = ath11k_hw_ipq8074_rx_desc_get_dot11_hdr, ++ .rx_desc_get_crypto_header = ath11k_hw_ipq8074_rx_desc_get_crypto_hdr, + }; + + const struct ath11k_hw_ops qca6390_ops = { +@@ -1071,6 +1293,13 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ .rx_desc_get_offset = ath11k_hw_ipq8074_rx_desc_get_offset, ++#endif ++ .rx_desc_get_mpdu_frame_ctl = ath11k_hw_ipq8074_rx_desc_get_mpdu_frame_ctl, ++ .rx_desc_dot11_hdr_fields_valid = ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid, ++ .rx_desc_get_dot11_hdr = ath11k_hw_ipq8074_rx_desc_get_dot11_hdr, ++ .rx_desc_get_crypto_header = ath11k_hw_ipq8074_rx_desc_get_crypto_hdr, + }; + + const struct ath11k_hw_ops qcn9074_ops = { +@@ -1108,10 +1337,17 @@ const struct ath11k_hw_ops qcn9074_ops = + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_ipq8074_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_qcn9074_mpdu_info_get_peerid, +- .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, +- .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, ++ .rx_desc_mac_addr2_valid = ath11k_hw_qcn9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_qcn9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len, ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ .rx_desc_get_offset = ath11k_hw_qcn9074_rx_desc_get_offset, ++#endif ++ .rx_desc_get_mpdu_frame_ctl = ath11k_hw_qcn9074_rx_desc_get_mpdu_frame_ctl, ++ .rx_desc_dot11_hdr_fields_valid = ath11k_hw_qcn9074_rx_desc_dot11_hdr_fields_valid, ++ .rx_desc_get_dot11_hdr = ath11k_hw_qcn9074_rx_desc_get_dot11_hdr, ++ .rx_desc_get_crypto_header = ath11k_hw_qcn9074_rx_desc_get_crypto_hdr, + }; + + const struct ath11k_hw_ops wcn6855_ops = { +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -22,6 +22,11 @@ + #define ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE 512 + #define ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE 128 + #define ATH11K_DP_RXDMA_MONITOR_DST_RING_SIZE 128 ++#define ATH11K_DP_RXDMA_REFILL_RING_SIZE 2048 ++/* 256b desc TLV + 4b(rounded) Pad + 30byte max nwifi header + ++ * 18byte mesh hdr + 8byte snap + 1500 eth payload ++ */ ++#define ATH11K_DP_RXDMA_NSS_REFILL_RING_SIZE 1816 + #else + /* Num VDEVS per radio */ + #define TARGET_NUM_VDEVS(ab) (ab->hw_params.num_vdevs_peers[ab->qmi.target_mem_mode].num_vdevs) +@@ -33,6 +38,8 @@ + #define ATH11K_DP_RXDMA_MON_STATUS_RING_SIZE 1024 + #define ATH11K_DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 + #define ATH11K_DP_RXDMA_MONITOR_DST_RING_SIZE 2048 ++#define ATH11K_DP_RXDMA_REFILL_RING_SIZE 2048 ++#define ATH11K_DP_RXDMA_NSS_REFILL_RING_SIZE 2048 + #endif + + /* Num of peers for Single Radio mode */ +@@ -129,6 +136,9 @@ enum ath11k_bus { + struct hal_rx_desc; + struct hal_tcl_data_cmd; + ++struct htt_rx_ring_tlv_filter; ++enum hal_encrypt_type; ++ + struct ath11k_hw_ring_mask { + u8 tx[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_mon_status[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +@@ -287,6 +297,16 @@ struct ath11k_hw_ops { + u8* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc); + u32 (*get_ring_selector)(struct sk_buff *skb); + u32 (*rx_desc_get_hal_mpdu_len)(struct hal_rx_mpdu_info *mpdu_info); ++#ifdef CPTCFG_ATH11K_MEM_PROFILE_512M ++ void (*rx_desc_get_offset)(struct htt_rx_ring_tlv_filter *tlv_filter); ++#endif ++ u16 (*rx_desc_get_mpdu_frame_ctl)(struct hal_rx_desc *desc); ++ bool (*rx_desc_dot11_hdr_fields_valid)(struct hal_rx_desc *desc); ++ void (*rx_desc_get_dot11_hdr)(struct hal_rx_desc *desc, ++ struct ieee80211_hdr *hdr); ++ void (*rx_desc_get_crypto_header)(struct hal_rx_desc *desc, ++ u8 *crypto_hdr, ++ enum hal_encrypt_type enctype); + }; + + extern const struct ath11k_hw_ops ipq8074_ops; +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6450,6 +6450,7 @@ static int ath11k_mac_config_mon_status_ + tlv_filter.rx_filter = ath11k_debugfs_rx_filter(ar); + } + ++ tlv_filter.offset_valid = false; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, +--- a/drivers/net/wireless/ath/ath11k/rx_desc.h ++++ b/drivers/net/wireless/ath/ath11k/rx_desc.h +@@ -1442,9 +1442,11 @@ struct hal_rx_desc_ipq8074 { + __le32 mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + u8 rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; ++#endif + u8 msdu_payload[]; + } __packed; + +@@ -1461,9 +1463,11 @@ struct hal_rx_desc_qcn9074 { + __le32 mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + u8 rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; ++#endif + u8 msdu_payload[]; + } __packed; + +@@ -1480,9 +1484,11 @@ struct hal_rx_desc_wcn6855 { + __le32 mpdu_end_tag; + struct rx_mpdu_end mpdu_end; + u8 rx_padding1[HAL_RX_DESC_PADDING1_BYTES]; ++#ifndef CPTCFG_ATH11K_MEM_PROFILE_512M + __le32 hdr_status_tag; + __le32 phy_ppdu_id; + u8 hdr_status[HAL_RX_DESC_HDR_STATUS_LEN]; ++#endif + u8 msdu_payload[]; + } __packed; + +@@ -1507,4 +1513,17 @@ struct hal_rx_desc { + #define RU_484 18 + #define RU_996 37 + ++#define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \ ++ FIELD_GET(GENMASK(7, 0), __le32_to_cpu(__val)) ++ ++#define HAL_RX_MPDU_INFO_PN_GET_BYTE2(__val) \ ++ FIELD_GET(GENMASK(15, 8), __le32_to_cpu(__val)) ++ ++#define HAL_RX_MPDU_INFO_PN_GET_BYTE3(__val) \ ++ FIELD_GET(GENMASK(23, 16), __le32_to_cpu(__val)) ++ ++#define HAL_RX_MPDU_INFO_PN_GET_BYTE4(__val) \ ++ FIELD_GET(GENMASK(31, 24), __le32_to_cpu(__val)) ++ ++ + #endif /* ATH11K_RX_DESC_H */ +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -4360,7 +4360,7 @@ static int ath11k_nss_init(struct ath11k + + /* fill rx parameters to initialize rx context */ + wim->wrip.tlv_size = ab->hw_params.hal_desc_sz; +- wim->wrip.rx_buf_len = DP_RX_BUFFER_SIZE; ++ wim->wrip.rx_buf_len = DP_RXDMA_NSS_REFILL_RING_SIZE; + + /* fill hal srng message */ + wim->hssm.dev_base_addr = (u32)ab->mem_pa; diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-311-ath11k-configure-nss-thread-priority-during-pdev_ini.patch b/package/kernel/mac80211/patches/nss/ath11k/999-311-ath11k-configure-nss-thread-priority-during-pdev_ini.patch new file mode 100644 index 00000000000000..c95960f2de04bf --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-311-ath11k-configure-nss-thread-priority-during-pdev_ini.patch @@ -0,0 +1,109 @@ +From 246e530a47d9adab9106fb6f2b92197cace17e53 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Fri, 21 May 2021 14:16:22 +0530 +Subject: [PATCH] ath11k: configure nss radio priority during pdev_init + +pdev's priority value is read from dts. Get scheme_id +using pdev priority. Configure scheme_id during pdev_init. + +Signed-off-by: Seevalamuthu Mariappan +--- + drivers/net/wireless/ath/ath11k/nss.c | 20 +++++++++++++++++++- + drivers/net/wireless/ath/ath11k/nss.h | 3 +++ + 2 files changed, 22 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -2,6 +2,7 @@ + /* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ ++#include + + #include "debug.h" + #include "mac.h" +@@ -4342,6 +4343,7 @@ static int ath11k_nss_init(struct ath11k + nss_tx_status_t status; + struct ath11k_dp *dp; + int i, ret; ++ struct device *dev = ab->dev; + + dp = &ab->dp; + +@@ -4361,6 +4363,8 @@ static int ath11k_nss_init(struct ath11k + /* fill rx parameters to initialize rx context */ + wim->wrip.tlv_size = ab->hw_params.hal_desc_sz; + wim->wrip.rx_buf_len = DP_RXDMA_NSS_REFILL_RING_SIZE; ++ if (of_property_read_bool(dev->of_node, "nss-radio-priority")) ++ wim->flags |= WIFILI_MULTISOC_THREAD_MAP_ENABLE; + + /* fill hal srng message */ + wim->hssm.dev_base_addr = (u32)ab->mem_pa; +@@ -4549,11 +4553,13 @@ int ath11k_nss_pdev_init(struct ath11k_b + struct nss_wifili_msg *wlmsg = NULL; + nss_wifili_msg_callback_t msg_cb; + nss_tx_status_t status; ++ struct device *dev = ab->dev; + int radio_if_num = -1; + int refill_ring_id; + int features = 0; + int dyn_if_type; +- int ret, i; ++ int ret, i, scheme_id = 0; ++ u32 nss_radio_priority; + + dyn_if_type = ath11k_nss_get_dynamic_interface_type(ab); + +@@ -4582,6 +4588,15 @@ int ath11k_nss_pdev_init(struct ath11k_b + ath11k_dbg(ab, ATH11K_DBG_NSS, "nss pdev init - id:%d init ctxt:%p ifnum:%d\n", + ar->pdev->pdev_id, ar->nss.ctx, ar->nss.if_num); + ++ if (!of_property_read_u32(dev->of_node, "nss-radio-priority", &nss_radio_priority)) { ++ scheme_id = nss_wifili_thread_scheme_alloc(ab->nss.ctx, ar->nss.if_num, nss_radio_priority); ++ if (scheme_id == WIFILI_SCHEME_ID_INVALID) { ++ ath11k_warn(ab, "received invalid scheme_id, configuring default value\n"); ++ scheme_id = 0; ++ } ++ } ++ ath11k_dbg(ab, ATH11K_DBG_NSS, "ifnum: %d scheme_id: %d nss_radio_priority: %d\n", ar->nss.if_num, scheme_id, nss_radio_priority); ++ + wlmsg = kzalloc(sizeof(struct nss_wifili_msg), GFP_ATOMIC); + if (!wlmsg) { + ret = -ENOMEM; +@@ -4594,6 +4609,7 @@ int ath11k_nss_pdev_init(struct ath11k_b + pdevmsg->lmac_id = ar->lmac_id; + pdevmsg->target_pdev_id = ar->pdev->pdev_id; + pdevmsg->num_rx_swdesc = WIFILI_RX_DESC_POOL_WEIGHT * DP_RXDMA_BUF_RING_SIZE; ++ pdevmsg->scheme_id = scheme_id; + + /* Store rxdma ring info to the message */ + refill_ring_id = ar->dp.rx_refill_buf_ring.refill_buf_ring.ring_id; +@@ -4887,6 +4903,9 @@ int ath11k_nss_pdev_deinit(struct ath11k + /* pdev deinit msg success, dealloc, deregister and return */ + ret = 0; + ++ /* reset thread scheme*/ ++ nss_wifili_thread_scheme_dealloc(ab->nss.ctx, ar->nss.if_num); ++ + nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type); + nss_unregister_wifili_radio_if(ar->nss.if_num); + free: +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -70,6 +70,7 @@ struct hal_rx_user_status; + /* Init Flags */ + #define WIFILI_NSS_CCE_DISABLED 0x1 + #define WIFILI_ADDTL_MEM_SEG_SET 0x000000002 ++#define WIFILI_MULTISOC_THREAD_MAP_ENABLE 0x10 + + /* ATH11K NSS PEER Info */ + /* Host memory allocated for peer info storage in nss */ +@@ -122,6 +123,8 @@ enum ath11k_nss_vdev_cmd { + /* Enables the MCBC exception in NSS fw, 1 = enable */ + #define ATH11K_NSS_ENABLE_MCBC_EXC 1 + ++#define WIFILI_SCHEME_ID_INVALID -1 ++ + enum ath11k_nss_opmode { + ATH11K_NSS_OPMODE_UNKNOWN, + ATH11K_NSS_OPMODE_AP, diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-312-ath11k-add-ampdu-id-in-802.11-radiotap-header.patch b/package/kernel/mac80211/patches/nss/ath11k/999-312-ath11k-add-ampdu-id-in-802.11-radiotap-header.patch new file mode 100644 index 00000000000000..12a035bf4e5790 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-312-ath11k-add-ampdu-id-in-802.11-radiotap-header.patch @@ -0,0 +1,279 @@ +From 3f962ed9a4079964c48e321fd928a2719038d881 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 28 May 2021 23:53:57 +0530 +Subject: [PATCH] ath11k: add ampdu id in 802.11 radiotap header + +AMPDU aggregate reference number is generated by +driver internally which is same across each +subframe of an ampdu. + +For fetching AMPDU-ID, we need to concatenate +ppdu_id from mpdu_info and tlv_usr from tlv heder. +while parsing monitor TLV data with HAL_RX_MPDU_START +TLV tag, ampdu id is fetched from mpdu_info and +updated to corresponding mac80211 structure during +ath11k_update_radiotap. + +Signed-off-by: P Praneesh +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hal_rx.c | 13 ++++++++++++ + drivers/net/wireless/ath/ath11k/hal_rx.h | 16 ++++++++------ + drivers/net/wireless/ath/ath11k/hw.c | 36 +++++++++++++++++++++++++------- + drivers/net/wireless/ath/ath11k/hw.h | 1 + + 5 files changed, 58 insertions(+), 14 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -5995,6 +5995,7 @@ static void ath11k_update_radiotap(struc + { + struct ieee80211_supported_band *sband; + u8 *ptr = NULL; ++ u16 ampdu_id = ppduinfo->ampdu_id[ppduinfo->userid]; + + rxs->flag |= RX_FLAG_MACTIME_START; + rxs->signal = ppduinfo->rssi_comb + ATH11K_DEFAULT_NOISE_FLOOR; +@@ -6002,6 +6003,11 @@ static void ath11k_update_radiotap(struc + if (ppduinfo->nss) + rxs->nss = ppduinfo->nss; + ++ if (ampdu_id) { ++ rxs->flag |= RX_FLAG_AMPDU_DETAILS; ++ rxs->ampdu_reference = ampdu_id; ++ } ++ + if (ppduinfo->he_mu_flags) { + rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; + rxs->encoding = RX_ENC_HE; +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -873,6 +873,13 @@ static u16 ath11k_hal_rx_mpduinfo_get_pe + return ab->hw_params.hw_ops->mpdu_info_get_peerid(mpdu_info); + } + ++static ++u16 ath11k_hal_rxdesc_get_hal_mpdu_ppdu_id(struct ath11k_base *ab, ++ struct hal_rx_mpdu_info *mpdu_info) ++{ ++ return ab->hw_params.hw_ops->rx_desc_get_hal_ppdu_id(mpdu_info); ++} ++ + static enum hal_rx_mon_status + ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, + struct hal_rx_mon_ppdu_info *ppdu_info, +@@ -1546,6 +1553,12 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + + ppdu_info->mpdu_len += ab->hw_params.hw_ops->rx_desc_get_hal_mpdu_len(mpdu_info); + ++ if (userid < HAL_MAX_UL_MU_USERS) { ++ ppdu_info->userid = userid; ++ ppdu_info->ampdu_id[userid] = ++ ath11k_hal_rxdesc_get_hal_mpdu_ppdu_id(ab, mpdu_info); ++ } ++ + break; + } + case HAL_RXPCU_PPDU_END_INFO: { +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -221,6 +221,8 @@ struct hal_rx_mon_ppdu_info { + u32 num_users; + u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; + struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; ++ u8 userid; ++ u16 ampdu_id[HAL_MAX_UL_MU_USERS]; + bool ppdu_continuation; + }; + +@@ -460,20 +462,22 @@ struct hal_rx_phyrx_rssi_legacy_info { + #define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16) + #define HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855 GENMASK(15, 0) + #define HAL_RX_MPDU_INFO_INFO1_MPDU_LEN GENMASK(13, 0) ++#define HAL_RX_MPDU_INFO_INFO0_PPDU_ID GENMASK(31, 16) + + struct hal_rx_mpdu_info_ipq8074 { +- __le32 rsvd0; + __le32 info0; +- __le32 rsvd1[11]; + __le32 info1; ++ __le32 rsvd1[11]; ++ __le32 info2; + __le32 rsvd2[9]; + } __packed; + + struct hal_rx_mpdu_info_qcn9074 { +- __le32 rsvd0[10]; ++ __le32 rsvd0[9]; + __le32 info0; +- __le32 rsvd1[2]; + __le32 info1; ++ __le32 rsvd1[2]; ++ __le32 info2; + __le32 rsvd2[9]; + } __packed; + +@@ -493,9 +497,11 @@ struct hal_rx_mpdu_info { + + #define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) + struct hal_rx_ppdu_end_duration { +- __le32 rsvd0[9]; ++ __le32 rsvd0[2]; + __le32 info0; +- __le32 rsvd1[4]; ++ __le32 rsvd1[6]; ++ __le32 info1; ++ __le32 rsvd2[4]; + } __packed; + + struct hal_rx_rxpcu_classification_overview { +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -307,6 +307,7 @@ struct ath11k_hw_ops { + void (*rx_desc_get_crypto_header)(struct hal_rx_desc *desc, + u8 *crypto_hdr, + enum hal_encrypt_type enctype); ++ u16 (*rx_desc_get_hal_ppdu_id) (struct hal_rx_mpdu_info *mpdu_info); + }; + + extern const struct ath11k_hw_ops ipq8074_ops; +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -13,6 +13,7 @@ + #include "hif.h" + #include "hal.h" + #include "hw.h" ++#include "hal_rx.h" + + /* Map from pdev index to hw mac index */ + static u8 ath11k_hw_ipq8074_mac_from_pdev_id(int pdev_idx) +@@ -717,17 +718,6 @@ static u8 *ath11k_hw_qcn9074_rx_desc_get + return &desc->u.qcn9074.msdu_payload[0]; + } + +-static bool ath11k_hw_ipq9074_rx_desc_mac_addr2_valid(struct hal_rx_desc *desc) +-{ +- return __le32_to_cpu(desc->u.qcn9074.mpdu_start.info11) & +- RX_MPDU_START_INFO11_MAC_ADDR2_VALID; +-} +- +-static u8 *ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) +-{ +- return desc->u.qcn9074.mpdu_start.addr2; +-} +- + static bool ath11k_hw_wcn6855_rx_desc_get_first_msdu(struct hal_rx_desc *desc) + { + return !!FIELD_GET(RX_MSDU_END_INFO2_FIRST_MSDU_WCN6855, +@@ -1085,12 +1075,27 @@ static void ath11k_hw_ipq5018_reo_setup( + } + + static u16 ++ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_ppdu_id(struct hal_rx_mpdu_info *mpdu_info) ++{ ++ ++ return FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PPDU_ID, ++ __le32_to_cpu(mpdu_info->u.ipq8074.info0)); ++} ++ ++static ++u16 ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_ppdu_id(struct hal_rx_mpdu_info *mpdu_info) ++{ ++ ++ return FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PPDU_ID, ++ __le32_to_cpu(mpdu_info->u.qcn9074.info0)); ++} ++ ++static u16 + ath11k_hw_ipq8074_mpdu_info_get_peerid(struct hal_rx_mpdu_info *mpdu_info) + { + u16 peer_id = 0; +- + peer_id = FIELD_GET(HAL_RX_MPDU_INFO_INFO0_PEERID, +- __le32_to_cpu(mpdu_info->u.ipq8074.info0)); ++ __le32_to_cpu(mpdu_info->u.ipq8074.info1)); + + return peer_id; + } +@@ -1196,6 +1201,7 @@ const struct ath11k_hw_ops ipq8074_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq8074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_ppdu_id, + #ifdef CPTCFG_ATH11K_MEM_PROFILE_512M + .rx_desc_get_offset = ath11k_hw_ipq8074_rx_desc_get_offset, + #endif +@@ -1219,6 +1225,7 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_get_encrypt_type = ath11k_hw_ipq8074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_ipq8074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_ipq8074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ip_valid = ath11k_hw_ipq8074_rx_desc_get_ip_valid, + .rx_desc_get_ldpc_support = ath11k_hw_ipq8074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_ipq8074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_ipq8074_rx_desc_get_mpdu_fc_valid, +@@ -1251,6 +1258,7 @@ const struct ath11k_hw_ops ipq6018_ops = + .rx_desc_dot11_hdr_fields_valid = ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid, + .rx_desc_get_dot11_hdr = ath11k_hw_ipq8074_rx_desc_get_dot11_hdr, + .rx_desc_get_crypto_header = ath11k_hw_ipq8074_rx_desc_get_crypto_hdr, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_ppdu_id, + }; + + const struct ath11k_hw_ops qca6390_ops = { +@@ -1300,6 +1308,7 @@ const struct ath11k_hw_ops qca6390_ops = + .rx_desc_dot11_hdr_fields_valid = ath11k_hw_ipq8074_rx_desc_dot11_hdr_fields_valid, + .rx_desc_get_dot11_hdr = ath11k_hw_ipq8074_rx_desc_get_dot11_hdr, + .rx_desc_get_crypto_header = ath11k_hw_ipq8074_rx_desc_get_crypto_hdr, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_ppdu_id, + }; + + const struct ath11k_hw_ops qcn9074_ops = { +@@ -1341,6 +1350,7 @@ const struct ath11k_hw_ops qcn9074_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_qcn9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_ppdu_id, + #ifdef CPTCFG_ATH11K_MEM_PROFILE_512M + .rx_desc_get_offset = ath11k_hw_qcn9074_rx_desc_get_offset, + #endif +@@ -1389,6 +1399,7 @@ const struct ath11k_hw_ops wcn6855_ops = + .rx_desc_mpdu_start_addr2 = ath11k_hw_wcn6855_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_len, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_ipq8074_rx_desc_get_hal_mpdu_ppdu_id, + }; + + const struct ath11k_hw_ops wcn6750_ops = { +@@ -1405,6 +1416,7 @@ const struct ath11k_hw_ops wcn6750_ops = + .rx_desc_get_encrypt_type = ath11k_hw_qcn9074_rx_desc_get_encrypt_type, + .rx_desc_get_decap_type = ath11k_hw_qcn9074_rx_desc_get_decap_type, + .rx_desc_get_mesh_ctl = ath11k_hw_qcn9074_rx_desc_get_mesh_ctl, ++ .rx_desc_get_ip_valid = ath11k_hw_qcn9074_rx_desc_get_ip_valid, + .rx_desc_get_ldpc_support = ath11k_hw_qcn9074_rx_desc_get_ldpc_support, + .rx_desc_get_mpdu_seq_ctl_vld = ath11k_hw_qcn9074_rx_desc_get_mpdu_seq_ctl_vld, + .rx_desc_get_mpdu_fc_valid = ath11k_hw_qcn9074_rx_desc_get_mpdu_fc_valid, +@@ -1426,9 +1438,10 @@ const struct ath11k_hw_ops wcn6750_ops = + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .reo_setup = ath11k_hw_wcn6855_reo_setup, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, +- .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, +- .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, ++ .rx_desc_mac_addr2_valid = ath11k_hw_qcn9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_qcn9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_wcn6750_get_tcl_ring_selector, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_ppdu_id, + }; + + /* IPQ5018 hw ops is similar to QCN9074 except for the dest ring remap */ +@@ -1468,10 +1481,11 @@ const struct ath11k_hw_ops ipq5018_ops = + .reo_setup = ath11k_hw_ipq5018_reo_setup, + .rx_desc_get_msdu_payload = ath11k_hw_qcn9074_rx_desc_get_msdu_payload, + .mpdu_info_get_peerid = ath11k_hw_ipq8074_mpdu_info_get_peerid, +- .rx_desc_mac_addr2_valid = ath11k_hw_ipq9074_rx_desc_mac_addr2_valid, +- .rx_desc_mpdu_start_addr2 = ath11k_hw_ipq9074_rx_desc_mpdu_start_addr2, ++ .rx_desc_mac_addr2_valid = ath11k_hw_qcn9074_rx_desc_mac_addr2_valid, ++ .rx_desc_mpdu_start_addr2 = ath11k_hw_qcn9074_rx_desc_mpdu_start_addr2, + .get_ring_selector = ath11k_hw_ipq8074_get_tcl_ring_selector, + .rx_desc_get_hal_mpdu_len = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_len, ++ .rx_desc_get_hal_ppdu_id = ath11k_hw_qcn9074_rx_desc_get_hal_mpdu_ppdu_id, + }; + + #define ATH11K_TX_RING_MASK_0 BIT(0) diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-336-0001-ath11k-idr-optimization.patch b/package/kernel/mac80211/patches/nss/ath11k/999-336-0001-ath11k-idr-optimization.patch new file mode 100644 index 00000000000000..ef01c639a266b4 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-336-0001-ath11k-idr-optimization.patch @@ -0,0 +1,132 @@ +From 58c0d08408e58f0f496127a59465726457dc72c8 Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam +Date: Mon, 15 Nov 2021 17:51:43 +0530 +Subject: [PATCH] ath11k: idr optimization + +Replace idr_find and idr_remove with idr_remove. As idr_remove +itself will do idr_find. And use dma low level api. + +Signed-off-by: Tamizh Chelvam +--- + backport-include/linux/idr.h | 4 +++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 52 +++++++++++---------------------- + drivers/net/wireless/ath/ath11k/dp_tx.c | 2 +- + 3 files changed, 22 insertions(+), 36 deletions(-) + +--- a/backport-include/linux/idr.h ++++ b/backport-include/linux/idr.h +@@ -8,6 +8,10 @@ + static inline void *backport_idr_remove(struct idr *idr, int id) + { + void *item = idr_find(idr, id); ++ ++ if (!item) ++ return NULL; ++ + idr_remove(idr, id); + return item; + } +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -3385,18 +3385,16 @@ try_again: + ar = ab->pdevs[mac_id].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); + if (unlikely(!msdu)) { + ath11k_warn(ab, "frame rx with invalid buf_id %d\n", + buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + +- idr_remove(&rx_ring->bufs_idr, buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); +- + rxcb = ATH11K_SKB_RXCB(msdu); ++ + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); +@@ -4667,17 +4665,14 @@ ath11k_dp_process_rx_err_buf(struct ath1 + u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz; + + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_warn(ar->ab, "rx err buf with invalid buf_id %d\n", + buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + return -EINVAL; + } + +- idr_remove(&rx_ring->bufs_idr, buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); +- + rxcb = ATH11K_SKB_RXCB(msdu); + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), +@@ -5083,18 +5078,16 @@ int ath11k_dp_rx_process_wbm_err(struct + rx_ring = &ar->dp.rx_refill_buf_ring; + + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_warn(ab, "frame rx with invalid buf_id %d pdev %d\n", + buf_id, mac_id); +- spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + +- idr_remove(&rx_ring->bufs_idr, buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); +- + rxcb = ATH11K_SKB_RXCB(msdu); ++ + dma_unmap_single(ab->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); +@@ -5209,16 +5202,14 @@ int ath11k_dp_process_rxdma_err(struct a + msdu_cookies[i]); + + spin_lock_bh(&rx_ring->idr_lock); +- skb = idr_find(&rx_ring->bufs_idr, buf_id); ++ skb = idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); + if (!skb) { + ath11k_warn(ab, "rxdma error with invalid buf_id %d\n", + buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + continue; + } + +- idr_remove(&rx_ring->bufs_idr, buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(skb); + dma_unmap_single(ab->dev, rxcb->paddr, +@@ -6429,16 +6420,14 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct at + msdu_list.sw_cookie[i]); + + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_dbg(ar->ab, ATH11K_DBG_DATA, + "full mon msdu_pop: invalid buf_id %d\n", + buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + break; + } +- idr_remove(&rx_ring->bufs_idr, buf_id); +- spin_unlock_bh(&rx_ring->idr_lock); + + rxcb = ATH11K_SKB_RXCB(msdu); + if (!rxcb->unmapped) { diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-336-0002-ath11k-Use-idr_replace.patch b/package/kernel/mac80211/patches/nss/ath11k/999-336-0002-ath11k-Use-idr_replace.patch new file mode 100644 index 00000000000000..974de141070d63 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-336-0002-ath11k-Use-idr_replace.patch @@ -0,0 +1,280 @@ +From 93abe1755de2727bf8fb5969bce25ae49c704484 Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam +Date: Mon, 15 Nov 2021 18:15:38 +0530 +Subject: [PATCH] ath11k: Use idr_replace + +idr_alloc has been done multiple times upon reaping the msdu +using idr_remove. This idr_alloc would take more cpu, to redue +the cpu usage call idr_replace by storing used buf_ids instead +of calling idr_alloc during replenish. + +Signed-off-by: Tamizh Chelvam +--- + drivers/net/wireless/ath/ath11k/core.h | 6 +++ + drivers/net/wireless/ath/ath11k/dp.c | 2 +- + drivers/net/wireless/ath/ath11k/dp_rx.c | 66 +++++++++++++++++++++++++-------- + drivers/net/wireless/ath/ath11k/dp_rx.h | 2 +- + 4 files changed, 59 insertions(+), 17 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -662,6 +662,11 @@ struct ath11k_per_peer_tx_stats { + #define ATH11K_FLUSH_TIMEOUT (5 * HZ) + #define ATH11K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ) + ++struct ath11k_rx_buf_id { ++ struct list_head list; ++ int used_buf_id; ++}; ++ + struct ath11k { + struct ath11k_base *ab; + struct ath11k_pdev *pdev; +@@ -811,6 +816,7 @@ struct ath11k { + /* protected by conf_mutex */ + bool ps_state_enable; + bool ps_timekeeper_enable; ++ struct ath11k_rx_buf_id rx_buf_id; + }; + + struct ath11k_band_cap { +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -897,7 +897,7 @@ int ath11k_dp_service_srng(struct ath11k + + hal_params = ab->hw_params.hal_params; + ath11k_dp_rxbufs_replenish(ab, id, rx_ring, 0, +- hal_params->rx_buf_rbm); ++ hal_params->rx_buf_rbm, NULL); + } + } + } +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -388,7 +388,8 @@ static inline u8 ath11k_dp_rx_h_msdu_sta + int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, +- enum hal_rx_buf_return_buf_manager mgr) ++ enum hal_rx_buf_return_buf_manager mgr, ++ u32 *buf_ids) + { + struct hal_srng *srng; + u32 *desc; +@@ -396,9 +397,15 @@ int ath11k_dp_rxbufs_replenish(struct at + int num_free; + int num_remain; + int buf_id; ++ int buf_id_index; + u32 cookie; + dma_addr_t paddr; + ++ if (!buf_ids) ++ buf_id_index = 0; ++ else ++ buf_id_index = min(req_entries, DP_RX_MAX_IDR_BUF); ++ + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; +@@ -434,8 +441,14 @@ int ath11k_dp_rxbufs_replenish(struct at + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); ++ if (buf_ids && buf_id_index) { ++ buf_id_index--; ++ buf_id = buf_ids[buf_id_index]; ++ idr_replace(&rx_ring->bufs_idr, skb, buf_id); ++ } else { + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 1, + (rx_ring->bufs_max * 3) + 1, GFP_ATOMIC); ++ } + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id <= 0) + goto fail_dma_unmap; +@@ -458,6 +471,12 @@ int ath11k_dp_rxbufs_replenish(struct at + + spin_unlock_bh(&srng->lock); + ++ while (buf_id_index--) { ++ spin_lock_bh(&rx_ring->idr_lock); ++ idr_remove(&rx_ring->bufs_idr, buf_ids[buf_id_index]); ++ spin_unlock_bh(&rx_ring->idr_lock); ++ } ++ + return req_entries - num_remain; + + fail_idr_remove: +@@ -532,7 +551,7 @@ static int ath11k_dp_rxdma_ring_buf_setu + + rx_ring->bufs_max = num_entries; + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, +- ar->ab->hw_params.hal_params->rx_buf_rbm); ++ ar->ab->hw_params.hal_params->rx_buf_rbm, NULL); + return 0; + } + +@@ -3347,11 +3366,14 @@ int ath11k_dp_process_rx(struct ath11k_b + struct ath11k *ar; + struct hal_reo_dest_ring *desc; + enum hal_reo_dest_ring_push_reason push_reason; ++ u32 *rx_buf_id[MAX_RADIOS]; + u32 cookie; + int i; + +- for (i = 0; i < MAX_RADIOS; i++) ++ for (i = 0; i < MAX_RADIOS; i++) { + __skb_queue_head_init(&msdu_list[i]); ++ rx_buf_id[i] = kzalloc(sizeof(u32) * DP_RX_MAX_IDR_BUF, GFP_ATOMIC); ++ } + + srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; + +@@ -3384,8 +3406,15 @@ try_again: + + ar = ab->pdevs[mac_id].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; ++ i = num_buffs_reaped[mac_id]; ++ + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ if (rx_buf_id[mac_id] && i < DP_RX_MAX_IDR_BUF) { ++ msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ rx_buf_id[mac_id][i] = buf_id; ++ } else { ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ } + spin_unlock_bh(&rx_ring->idr_lock); + if (unlikely(!msdu)) { + ath11k_warn(ab, "frame rx with invalid buf_id %d\n", +@@ -3463,9 +3492,12 @@ try_again: + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], +- ab->hw_params.hal_params->rx_buf_rbm); ++ ab->hw_params.hal_params->rx_buf_rbm, rx_buf_id[i]); + } + exit: ++ for (i = 0; i < MAX_RADIOS; i++) ++ kfree(rx_buf_id[i]); ++ + return total_msdu_reaped; + } + +@@ -4827,7 +4859,7 @@ exit: + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, n_bufs_reaped[i], +- ab->hw_params.hal_params->rx_buf_rbm); ++ ab->hw_params.hal_params->rx_buf_rbm, NULL); + } + + return tot_n_bufs_reaped; +@@ -5043,14 +5075,17 @@ int ath11k_dp_rx_process_wbm_err(struct + struct sk_buff *msdu; + struct sk_buff_head msdu_list[MAX_RADIOS]; + struct ath11k_skb_rxcb *rxcb; ++ u32 *wbm_err_buf_id[MAX_RADIOS]; + u32 *rx_desc; + int buf_id, mac_id; + int num_buffs_reaped[MAX_RADIOS] = {0}; + int total_num_buffs_reaped = 0; + int ret, i; + +- for (i = 0; i < ab->num_radios; i++) ++ for (i = 0; i < ab->num_radios; i++) { + __skb_queue_head_init(&msdu_list[i]); ++ wbm_err_buf_id[i] = kzalloc(sizeof(u32) * DP_RX_MAX_IDR_BUF, GFP_ATOMIC); ++ } + + srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; + +@@ -5076,9 +5111,15 @@ int ath11k_dp_rx_process_wbm_err(struct + + ar = ab->pdevs[mac_id].ar; + rx_ring = &ar->dp.rx_refill_buf_ring; ++ i = num_buffs_reaped[mac_id]; + + spin_lock_bh(&rx_ring->idr_lock); +- msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ if (wbm_err_buf_id[mac_id] && i < DP_RX_MAX_IDR_BUF) { ++ msdu = idr_find(&rx_ring->bufs_idr, buf_id); ++ wbm_err_buf_id[mac_id][i] = buf_id; ++ } else { ++ msdu = idr_remove(&rx_ring->bufs_idr, buf_id); ++ } + spin_unlock_bh(&rx_ring->idr_lock); + if (!msdu) { + ath11k_warn(ab, "frame rx with invalid buf_id %d pdev %d\n", +@@ -5123,7 +5164,7 @@ int ath11k_dp_rx_process_wbm_err(struct + rx_ring = &ar->dp.rx_refill_buf_ring; + + ath11k_dp_rxbufs_replenish(ab, i, rx_ring, num_buffs_reaped[i], +- ab->hw_params.hal_params->rx_buf_rbm); ++ ab->hw_params.hal_params->rx_buf_rbm, wbm_err_buf_id[i]); + } + + rcu_read_lock(); +@@ -5145,6 +5186,8 @@ int ath11k_dp_rx_process_wbm_err(struct + } + rcu_read_unlock(); + done: ++ for (i = 0; i < ab->num_radios; i++) ++ kfree(wbm_err_buf_id[i]); + return total_num_buffs_reaped; + } + +@@ -5230,7 +5273,7 @@ int ath11k_dp_process_rxdma_err(struct a + + if (num_buf_freed) + ath11k_dp_rxbufs_replenish(ab, mac_id, rx_ring, num_buf_freed, +- ab->hw_params.hal_params->rx_buf_rbm); ++ ab->hw_params.hal_params->rx_buf_rbm, NULL); + + return budget - quota; + } +@@ -6182,12 +6225,12 @@ static void ath11k_dp_rx_mon_dest_proces + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rxdma_mon_buf_ring, + rx_bufs_used, +- hal_params->rx_buf_rbm); ++ hal_params->rx_buf_rbm, NULL); + else + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rx_refill_buf_ring, + rx_bufs_used, +- hal_params->rx_buf_rbm); ++ hal_params->rx_buf_rbm, NULL); + } + } + +@@ -6697,7 +6740,7 @@ next_entry: + ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, + &dp->rxdma_mon_buf_ring, + rx_bufs_used, +- HAL_RX_BUF_RBM_SW3_BM); ++ HAL_RX_BUF_RBM_SW3_BM, NULL); + } + + reap_status_ring: +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -11,6 +11,8 @@ + + #define DP_MAX_NWIFI_HDR_LEN 36 + ++#define DP_RX_MAX_IDR_BUF 256 ++ + #define DP_RX_MPDU_ERR_FCS BIT(0) + #define DP_RX_MPDU_ERR_DECRYPT BIT(1) + #define DP_RX_MPDU_ERR_TKIP_MIC BIT(2) +@@ -125,7 +127,8 @@ int ath11k_dp_process_rx(struct ath11k_b + int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id, + struct dp_rxdma_ring *rx_ring, + int req_entries, +- enum hal_rx_buf_return_buf_manager mgr); ++ enum hal_rx_buf_return_buf_manager mgr, ++ u32 *buf_id); + int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len, + int (*iter)(struct ath11k_base *ar, u16 tag, u16 len, + const void *ptr, void *data), diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-374-ath11k-Check-skb_headroom-before-using-skb_push.patch b/package/kernel/mac80211/patches/nss/ath11k/999-374-ath11k-Check-skb_headroom-before-using-skb_push.patch new file mode 100644 index 00000000000000..988717c1dbc257 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-374-ath11k-Check-skb_headroom-before-using-skb_push.patch @@ -0,0 +1,253 @@ +From efecc6e8355d02aeac7bf1a43397551440a8d0d8 Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam Raja +Date: Thu, 30 Mar 2023 22:12:56 +0530 +Subject: [PATCH] ath11k: Check skb_headroom before using skb_push + +Below kernel panic may occur if there is no +skb_headroom available for performing skb_push. + +<4>[67506.565072] CPU: 1 PID: 1741 Comm: ap-wireless-opt Not tainted 5.4.89+ #0 +<4>[67506.578860] Hardware name: Generic DT based system +<4>[67506.585728] PC is at fortify_panic+0x10/0x18 +<4>[67506.590406] LR is at fortify_panic+0x10/0x18 + +(fortify_panic) from [<7f2cd3cc>] (ath11k_dp_rx_crypto_icv_len+0x1e0/0x161c [ath11k]) +(ath11k_dp_rx_crypto_icv_len [ath11k]) from [<7f2cdbac>] (ath11k_dp_rx_crypto_icv_len+0x9c0/0x161c [ath11k]) +(ath11k_dp_rx_crypto_icv_len [ath11k]) from [<7f2ce0e8>] (ath11k_dp_rx_crypto_icv_len+0xefc/0x161c [ath11k]) +(ath11k_dp_rx_crypto_icv_len [ath11k]) from [<7f2cedb0>] (ath11k_dp_process_rx+0x4ec/0x544 [ath11k]) +(ath11k_dp_process_rx [ath11k]) from [<7f2c417c>] (ath11k_dp_service_srng+0xdc/0x2a0 [ath11k]) +(ath11k_dp_service_srng [ath11k]) from [<7f0d78c8>] (ath11k_pci_ext_grp_napi_poll+0x20/0x50 [ath11k_pci]) +(ath11k_pci_ext_grp_napi_poll [ath11k_pci]) from [<806a6a74>] (__napi_poll+0x28/0xb8) +(__napi_poll) from [<806a6c9c>] (net_rx_action+0xec/0x280) +(net_rx_action) from [<8010226c>] (__do_softirq+0xc4/0x248) +(__do_softirq) from [<8011f230>] (irq_exit+0x6c/0xcc) +(irq_exit) from [<80162a08>] (__handle_domain_irq+0x8c/0xb0) +(__handle_domain_irq) from [<803f0188>] (gic_handle_irq+0x54/0x8c) +(gic_handle_irq) from [<80101a8c>] (__irq_svc+0x6c/0xa8) + +Fix this by checking skb_headroom and expand the +headroom if required size is not available. + +Signed-off-by: Tamizh Chelvam Raja +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 70 +++++++++++++++++++++++++ + 1 file changed, 70 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -2454,16 +2454,27 @@ static void ath11k_get_dot11_hdr_from_rx + size_t hdr_len, crypto_len; + struct ieee80211_hdr *hdr; + u16 fc, qos_ctl = 0; ++ int expand_by; + u8 *crypto_hdr; + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + crypto_len = ath11k_dp_rx_crypto_param_len(ar, enctype); ++ if (skb_headroom(msdu) < crypto_len) { ++ expand_by = crypto_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + crypto_hdr = skb_push(msdu, crypto_len); + ath11k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype); + } + + fc = ath11k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc); + hdr_len = ieee80211_hdrlen(fc); ++ if (skb_headroom(msdu) < hdr_len) { ++ expand_by = hdr_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + skb_push(msdu, hdr_len); + hdr = (struct ieee80211_hdr *)msdu->data; + hdr->frame_control = fc; +@@ -2499,6 +2510,7 @@ static void ath11k_dp_rx_h_undecap_nwifi + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u16 qos_ctl = 0; ++ int expand_by = 0; + u8 *qos, *crypto_hdr; + bool add_qos_ctrl = false; + +@@ -2543,26 +2555,46 @@ static void ath11k_dp_rx_h_undecap_nwifi + } + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { ++ int crypto_param_len = ath11k_dp_rx_crypto_param_len(ar, enctype); ++ ++ if (skb_headroom(msdu) < crypto_param_len) { ++ expand_by = crypto_param_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + if (first_hdr) { +- memcpy(skb_push(msdu, +- ath11k_dp_rx_crypto_param_len(ar, enctype)), +- (void *)hdr + hdr_len, +- ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ memcpy(skb_push(msdu, crypto_param_len), ++ (void *)hdr + hdr_len, crypto_param_len); + } else { +- crypto_hdr = skb_push(msdu, ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ crypto_hdr = skb_push(msdu, crypto_param_len); + ath11k_dp_rx_desc_get_crypto_header(ar->ab, + rxcb->rx_desc, crypto_hdr, enctype); + } + } + + if (!rxcb->is_first_msdu || add_qos_ctrl) { ++ if (skb_headroom(msdu) < IEEE80211_QOS_CTL_LEN) { ++ expand_by = IEEE80211_QOS_CTL_LEN - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, + IEEE80211_QOS_CTL_LEN), &qos_ctl, + IEEE80211_QOS_CTL_LEN); ++ if (skb_headroom(msdu) < hdr_len) { ++ expand_by = hdr_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, hdr_len), decap_hdr, hdr_len); + return; + } + ++ if (skb_headroom(msdu) < hdr_len) { ++ expand_by = hdr_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + /* original 802.11 header has a different DA and in +@@ -2671,6 +2703,7 @@ static void ath11k_dp_rx_h_undecap_eth(s + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + void *rfc1042; ++ int expand_by; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct ath11k_dp_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}}; + +@@ -2680,6 +2713,11 @@ static void ath11k_dp_rx_h_undecap_eth(s + ether_addr_copy(sa, eth->h_source); + rfc.snap_type = eth->h_proto; + skb_pull(msdu, sizeof(struct ethhdr)); ++ if (skb_headroom(msdu) < sizeof(struct ath11k_dp_rfc1042_hdr)) { ++ expand_by = sizeof(struct ath11k_dp_rfc1042_hdr) - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), &rfc, + sizeof(struct ath11k_dp_rfc1042_hdr)); + ath11k_get_dot11_hdr_from_rx_desc(ar, msdu, rxcb, status, enctype); +@@ -2697,6 +2735,11 @@ static void ath11k_dp_rx_h_undecap_eth(s + skb_pull(msdu, sizeof(struct ethhdr)); + + /* push rfc1042/llc/snap */ ++ if (skb_headroom(msdu) < sizeof(struct ath11k_dp_rfc1042_hdr)) { ++ expand_by = sizeof(struct ath11k_dp_rfc1042_hdr) - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, sizeof(struct ath11k_dp_rfc1042_hdr)), rfc1042, + sizeof(struct ath11k_dp_rfc1042_hdr)); + +@@ -2705,12 +2748,22 @@ static void ath11k_dp_rx_h_undecap_eth(s + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { +- memcpy(skb_push(msdu, +- ath11k_dp_rx_crypto_param_len(ar, enctype)), +- (void *)hdr + hdr_len, +- ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ int crypto_param_len = ath11k_dp_rx_crypto_param_len(ar, enctype); ++ ++ if (skb_headroom(msdu) < crypto_param_len) { ++ expand_by = crypto_param_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } ++ memcpy(skb_push(msdu, crypto_param_len), ++ (void *)hdr + hdr_len, crypto_param_len); + } + ++ if (skb_headroom(msdu) < hdr_len) { ++ expand_by = hdr_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + + exit: +@@ -2731,6 +2784,7 @@ static void ath11k_dp_rx_h_undecap_snap( + struct ieee80211_hdr *hdr; + size_t hdr_len; + u8 l3_pad_bytes; ++ int expand_by; + struct hal_rx_desc *rx_desc; + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + +@@ -2755,12 +2809,22 @@ static void ath11k_dp_rx_h_undecap_snap( + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { +- memcpy(skb_push(msdu, +- ath11k_dp_rx_crypto_param_len(ar, enctype)), +- (void *)hdr + hdr_len, +- ath11k_dp_rx_crypto_param_len(ar, enctype)); ++ int crypto_param_len = ath11k_dp_rx_crypto_param_len(ar, enctype); ++ ++ if (skb_headroom(msdu) < crypto_param_len) { ++ expand_by = crypto_param_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } ++ memcpy(skb_push(msdu, crypto_param_len), ++ (void *)hdr + hdr_len, crypto_param_len); + } + ++ if (skb_headroom(msdu) < hdr_len) { ++ expand_by = hdr_len - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ return; ++ } + memcpy(skb_push(msdu, hdr_len), hdr, hdr_len); + } + +@@ -2889,7 +2953,7 @@ static void ath11k_dp_rx_h_mpdu(struct a + struct ieee80211_rx_status *rx_status, + bool *fast_rx) + { +- bool fill_crypto_hdr; ++ bool fill_crypto_hdr = 0; + enum hal_encrypt_type enctype; + bool is_decrypted = false; + struct ath11k_skb_rxcb *rxcb; +@@ -3122,10 +3186,16 @@ static void ath11k_dp_rx_deliver_msdu(st + u8 decap = DP_RX_DECAP_TYPE_RAW; + bool is_mcbc = rxcb->is_mcbc; + bool is_eapol = rxcb->is_eapol; ++ int expand_by; + + if (status->encoding == RX_ENC_HE && + !(status->flag & RX_FLAG_RADIOTAP_HE) && + !(status->flag & RX_FLAG_SKIP_MONITOR)) { ++ if (skb_headroom(msdu) < sizeof(known)) { ++ expand_by = sizeof(known) - skb_headroom(msdu); ++ if (WARN_ON_ONCE(pskb_expand_head(msdu, expand_by, 0, GFP_ATOMIC))) ++ goto exit; ++ } + he = skb_push(msdu, sizeof(known)); + memcpy(he, &known, sizeof(known)); + status->flag |= RX_FLAG_RADIOTAP_HE; +@@ -3181,6 +3251,7 @@ static void ath11k_dp_rx_deliver_msdu(st + !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) + rx_status->flag |= RX_FLAG_8023; + ++exit: + ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); + } + diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-783-001-wifi-ath11k-Fix-BCCA-counter-for-EMA.patch b/package/kernel/mac80211/patches/nss/ath11k/999-783-001-wifi-ath11k-Fix-BCCA-counter-for-EMA.patch new file mode 100644 index 00000000000000..cf712593cc15b8 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-783-001-wifi-ath11k-Fix-BCCA-counter-for-EMA.patch @@ -0,0 +1,115 @@ +From ea4988df80e62204c411a60bafadfbff23eaa773 Mon Sep 17 00:00:00 2001 +From: Rameshkumar Sundaram +Date: Thu, 15 Jun 2023 14:33:55 +0530 +Subject: [PATCH] wifi: ath11k: Fix BCCA counter for EMA + +Currently BCCA counter is updated to FW via csa counter offs and +beacon with new countdown is updated for every beacon tx completion event. +For EMA, all EMA beacons are updated in one shot, and counter update for +every tx event will mess up the actual sequence of countdown sent over the air. + +Allow FW to update the countdown till 1 and finalize the color +change. + +Signed-off-by: Rameshkumar Sundaram +--- + drivers/net/wireless/ath/ath11k/mac.c | 21 --------------------- + drivers/net/wireless/ath/ath11k/mac.h | 1 - + drivers/net/wireless/ath/ath11k/wmi.c | 23 +++++++++++++++-------- + 3 files changed, 15 insertions(+), 30 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -1601,27 +1601,6 @@ static int ath11k_mac_setup_bcn_tmpl(str + return ath11k_mac_setup_bcn_tmpl_mbssid(arvif); + } + +-void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) +-{ +- struct ieee80211_vif *vif = arvif->vif; +- +- if (!vif->bss_conf.color_change_active && !arvif->bcca_zero_sent) +- return; +- +- if (vif->bss_conf.color_change_active && +- ieee80211_beacon_cntdwn_is_complete(vif)) { +- arvif->bcca_zero_sent = true; +- ieee80211_color_change_finish(vif); +- return; +- } +- +- arvif->bcca_zero_sent = false; +- +- if (vif->bss_conf.color_change_active) +- ieee80211_beacon_update_cntdwn(vif); +- ath11k_mac_setup_bcn_tmpl(arvif); +-} +- + static void ath11k_control_beaconing(struct ath11k_vif *arvif, + struct ieee80211_bss_conf *info) + { +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -170,7 +170,6 @@ enum ath11k_supported_bw ath11k_mac_mac8 + enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); + void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); + void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id); +-void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif); + int ath11k_mac_wait_tx_complete(struct ath11k *ar); + int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, + enum wmi_sta_keepalive_method method, +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -1874,9 +1874,10 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *a + cmd->vdev_id = vdev_id; + cmd->tim_ie_offset = offs->tim_offset; + +- if (vif->bss_conf.csa_active) { ++ if (vif->bss_conf.csa_active || vif->bss_conf.color_change_active) { + cmd->csa_switch_count_offset = offs->cntdwn_counter_offs[0]; + cmd->ext_csa_switch_count_offset = offs->cntdwn_counter_offs[1]; ++ cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF); + } + + cmd->buf_len = bcn->len; +@@ -7586,7 +7587,6 @@ static void ath11k_bcn_tx_status_event(s + rcu_read_unlock(); + return; + } +- ath11k_mac_bcn_tx_event(arvif); + rcu_read_unlock(); + } + +@@ -8500,10 +8500,7 @@ ath11k_wmi_process_csa_switch_count_even + { + int i; + struct ath11k_vif *arvif; +- +- /* Finish CSA once the switch count becomes NULL */ +- if (ev->current_switch_count) +- return; ++ struct ieee80211_bss_conf *bss_conf; + + rcu_read_lock(); + for (i = 0; i < ev->num_vdevs; i++) { +@@ -8515,8 +8512,18 @@ ath11k_wmi_process_csa_switch_count_even + continue; + } + +- if (arvif->is_up && arvif->vif->bss_conf.csa_active) +- ieee80211_csa_finish(arvif->vif); ++ bss_conf = &arvif->vif->bss_conf; ++ if (arvif->is_up && (bss_conf->csa_active || bss_conf->color_change_active)) { ++ if (!ev->current_switch_count) { ++ if (bss_conf->csa_active) ++ ieee80211_csa_finish(arvif->vif); ++ } else if (ev->current_switch_count > 1) { ++ ieee80211_beacon_update_cntdwn(arvif->vif); ++ } else { ++ if (bss_conf->color_change_active) ++ ieee80211_color_change_finish(arvif->vif); ++ } ++ } + } + rcu_read_unlock(); + } diff --git a/package/kernel/mac80211/patches/nss/subsys/007-fix_compilation_issue.patch b/package/kernel/mac80211/patches/nss/subsys/007-fix_compilation_issue.patch new file mode 100644 index 00000000000000..7a9b58411d0e1c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/007-fix_compilation_issue.patch @@ -0,0 +1,134 @@ +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -9247,7 +9247,7 @@ void cfg80211_bss_flush(struct wiphy *wi + * @count: the number of TBTTs until the color change happens + * @color_bitmap: representations of the colors that the local BSS is aware of + */ +-int cfg80211_bss_color_notify(struct net_device *dev, ++int cfg80211_bss_color_notify(struct net_device *dev, gfp_t gfp, + enum nl80211_commands cmd, u8 count, + u64 color_bitmap); + +@@ -9257,9 +9257,9 @@ int cfg80211_bss_color_notify(struct net + * @color_bitmap: representations of the colors that the local BSS is aware of + */ + static inline int cfg80211_obss_color_collision_notify(struct net_device *dev, +- u64 color_bitmap) ++ u64 color_bitmap, gfp_t gfp) + { +- return cfg80211_bss_color_notify(dev, NL80211_CMD_OBSS_COLOR_COLLISION, ++ return cfg80211_bss_color_notify(dev, gfp, NL80211_CMD_OBSS_COLOR_COLLISION, + 0, color_bitmap); + } + +@@ -9273,7 +9273,7 @@ static inline int cfg80211_obss_color_co + static inline int cfg80211_color_change_started_notify(struct net_device *dev, + u8 count) + { +- return cfg80211_bss_color_notify(dev, NL80211_CMD_COLOR_CHANGE_STARTED, ++ return cfg80211_bss_color_notify(dev, GFP_KERNEL, NL80211_CMD_COLOR_CHANGE_STARTED, + count, 0); + } + +@@ -9285,7 +9285,7 @@ static inline int cfg80211_color_change_ + */ + static inline int cfg80211_color_change_aborted_notify(struct net_device *dev) + { +- return cfg80211_bss_color_notify(dev, NL80211_CMD_COLOR_CHANGE_ABORTED, ++ return cfg80211_bss_color_notify(dev, GFP_KERNEL, NL80211_CMD_COLOR_CHANGE_ABORTED, + 0, 0); + } + +@@ -9297,7 +9297,7 @@ static inline int cfg80211_color_change_ + */ + static inline int cfg80211_color_change_notify(struct net_device *dev) + { +- return cfg80211_bss_color_notify(dev, ++ return cfg80211_bss_color_notify(dev, GFP_KERNEL, + NL80211_CMD_COLOR_CHANGE_COMPLETED, + 0, 0); + } +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -4779,7 +4779,7 @@ void ieee80211_color_collision_detection + struct ieee80211_sub_if_data *sdata = link->sdata; + + sdata_lock(sdata); +- cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap); ++ cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap, GFP_KERNEL); + sdata_unlock(sdata); + } + +--- a/net/mac80211/debugfs_netdev.c ++++ b/net/mac80211/debugfs_netdev.c +@@ -889,7 +889,7 @@ void ieee80211_debugfs_add_netdev(struct + { + char buf[10+IFNAMSIZ]; + +- sprintf(buf, "netdev:%s", sdata->name); ++ snprintf(buf, 10 + IFNAMSIZ, "netdev:%s", sdata->name); + sdata->vif.debugfs_dir = debugfs_create_dir(buf, + sdata->local->hw.wiphy->debugfsdir); + sdata->debugfs.subdir_stations = debugfs_create_dir("stations", +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -1414,18 +1414,6 @@ static void __sta_info_destroy_part2(str + WARN_ON_ONCE(ret); + } + +- /* Flush queues before removing keys, as that might remove them +- * from hardware, and then depending on the offload method, any +- * frames sitting on hardware queues might be sent out without +- * any encryption at all. +- */ +- if (local->ops->set_key) { +- if (local->ops->flush_sta) +- drv_flush_sta(local, sta->sdata, sta); +- else +- ieee80211_flush_queues(local, sta->sdata, false); +- } +- + /* now keys can no longer be reached */ + ieee80211_free_sta_keys(local, sta); + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -19484,7 +19484,7 @@ void cfg80211_ch_switch_started_notify(s + } + EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); + +-int cfg80211_bss_color_notify(struct net_device *dev, ++int cfg80211_bss_color_notify(struct net_device *dev, gfp_t gfp, + enum nl80211_commands cmd, u8 count, + u64 color_bitmap) + { +@@ -19498,7 +19498,7 @@ int cfg80211_bss_color_notify(struct net + + trace_cfg80211_bss_color_notify(dev, cmd, count, color_bitmap); + +- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); ++ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + if (!msg) + return -ENOMEM; + +@@ -19521,7 +19521,7 @@ int cfg80211_bss_color_notify(struct net + genlmsg_end(msg, hdr); + + return genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), +- msg, 0, NL80211_MCGRP_MLME, GFP_KERNEL); ++ msg, 0, NL80211_MCGRP_MLME, gfp); + + nla_put_failure: + nlmsg_free(msg); +--- a/backport-include/linux/netdevice.h ++++ b/backport-include/linux/netdevice.h +@@ -70,6 +70,9 @@ static inline void netif_trans_update(st + (_dev)->needs_free_netdev = true; + #endif + ++#define netdev_tstats(dev) dev->tstats ++#define netdev_assign_tstats(dev, e) dev->tstats = (e); ++ + #if LINUX_VERSION_IS_LESS(4,15,0) + static inline int _bp_netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev) diff --git a/package/kernel/mac80211/patches/nss/subsys/146-mac80211-enable-TKIP-when-using-encapsulation-offloading.patch b/package/kernel/mac80211/patches/nss/subsys/146-mac80211-enable-TKIP-when-using-encapsulation-offloading.patch new file mode 100644 index 00000000000000..86b2f2169bb79f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/146-mac80211-enable-TKIP-when-using-encapsulation-offloading.patch @@ -0,0 +1,12 @@ +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4689,8 +4689,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + if (!key) + key = rcu_dereference(sdata->default_unicast_key); + +- if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || +- key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) ++ if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))) + goto skip_offload; + + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); diff --git a/package/kernel/mac80211/patches/nss/subsys/199-001-mac80211-add-nss-support.patch b/package/kernel/mac80211/patches/nss/subsys/199-001-mac80211-add-nss-support.patch new file mode 100644 index 00000000000000..ecdf4d15c0c896 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/199-001-mac80211-add-nss-support.patch @@ -0,0 +1,453 @@ +From 193bfea2185a0ee976f54812e41ace77e6ee85e4 Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Fri, 10 Jul 2020 12:46:12 +0530 +Subject: [PATCH 1/3] mac80211: add nss support + +Add Support for NSS Offload if the HW supports it. +New flag is introduced to indicate HW support for NSS +offload + +Signed-off-by: Sriram R +--- + include/net/mac80211.h | 13 +++++++++++++ + net/mac80211/debugfs.c | 1 + + net/mac80211/util.c | 16 ++++++++++++++++ + 3 files changed, 30 insertions(+) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -380,6 +380,20 @@ enum ieee80211_bss_change { + /* when adding here, make sure to change ieee80211_reconfig */ + }; + ++/** ++ * enum ieee80211_nss_bss_change - NSS BSS change notification flags ++ * ++ * These flags are used with the nss_bss_info_changed() callback ++ * to indicate which NSS BSS parameter changed. ++ * ++ * @BSS_CHANGED_NSS_AP_ISOLATE: AP Isolate feature in NSS mode ++ * ++ */ ++ ++enum ieee80211_nss_bss_change { ++ BSS_CHANGED_NSS_AP_ISOLATE = BIT(0), ++}; ++ + /* + * The maximum number of IPv4 addresses listed for ARP filtering. If the number + * of addresses for an interface increase beyond this value, hardware ARP +@@ -683,6 +697,7 @@ struct ieee80211_fils_discovery { + * beamformee + * @eht_mu_beamformer: in AP-mode, does this BSS enable operation as an EHT MU + * beamformer ++ * @nss_ap_isolate: Used for notifying the NSS host about AP isolate feature + */ + struct ieee80211_bss_conf { + struct ieee80211_vif *vif; +@@ -776,6 +791,7 @@ struct ieee80211_bss_conf { + bool eht_su_beamformer; + bool eht_su_beamformee; + bool eht_mu_beamformer; ++ bool nss_ap_isolate; + }; + + /** +@@ -1410,7 +1426,7 @@ ieee80211_tx_info_clear_status(struct ie + * @RX_FLAG_AMPDU_EOF_BIT_KNOWN: The EOF value is known + * @RX_FLAG_RADIOTAP_HE: HE radiotap data is present + * (&struct ieee80211_radiotap_he, mac80211 will fill in +- * ++ * + * - DATA3_DATA_MCS + * - DATA3_DATA_DCM + * - DATA3_CODING +@@ -1418,7 +1434,7 @@ ieee80211_tx_info_clear_status(struct ie + * - DATA5_DATA_BW_RU_ALLOC + * - DATA6_NSTS + * - DATA3_STBC +- * ++ * + * from the RX info data, so leave those zeroed when building this data) + * @RX_FLAG_RADIOTAP_HE_MU: HE MU radiotap data is present + * (&struct ieee80211_radiotap_he_mu) +@@ -1991,6 +2007,16 @@ static inline bool lockdep_vif_mutex_hel + lockdep_vif_mutex_held(vif)) + + /** ++ * ieee80211_vif_to_wdev_relaxed - return a wdev struct from a vif ++ * @vif: the vif to get the wdev for ++ * ++ * This function is similar to ieee80211_vif_to_wdev, but the wdev ++ * is returned even if sdata is not running. ++ * ++ */ ++struct wireless_dev *ieee80211_vif_to_wdev_relaxed(struct ieee80211_vif *vif); ++ ++/** + * enum ieee80211_key_flags - key flags + * + * These flags are used for communication about keys between the driver +@@ -2682,6 +2708,8 @@ struct ieee80211_txq { + * @IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX: Hardware/driver handles transmitting + * multicast frames on all links, mac80211 should not do that. + * ++ * @IEEE80211_HW_SUPPORTS_NSS_OFFLOAD: Hardware/driver supports NSS offload ++ * + * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays + */ + enum ieee80211_hw_flags { +@@ -2739,6 +2767,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP, + IEEE80211_HW_DETECTS_COLOR_COLLISION, + IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX, ++ IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +@@ -3751,6 +3780,10 @@ struct ieee80211_prep_tx_info { + * non-MLO connections. + * The callback can sleep. + * ++ * @nss_bss_info_changed: Handler for configuration requests related to NSS BSS ++ * parameters that may vary during BSS's lifespan, and may affect low level ++ * driver. ++ * + * @prepare_multicast: Prepare for multicast filter configuration. + * This callback is optional, and its return value is passed + * to configure_filter(). This callback must be atomic. +@@ -4302,7 +4335,9 @@ struct ieee80211_ops { + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed); +- ++ void (*nss_bss_info_changed)(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ u32 changed); + int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); + void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +@@ -4607,7 +4642,7 @@ struct ieee80211_ops { + int (*reset_tid_config)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u8 tids); +- void (*update_vif_offload)(struct ieee80211_hw *hw, ++ int (*update_vif_offload)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -505,6 +505,7 @@ static const char *hw_flag_names[] = { + FLAG(SUPPORTS_CONC_MON_RX_DECAP), + FLAG(DETECTS_COLOR_COLLISION), + FLAG(MLO_MCAST_MULTI_LINK_TX), ++ FLAG(SUPPORTS_NSS_OFFLOAD), + #undef FLAG + }; + +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -868,6 +868,22 @@ struct wireless_dev *ieee80211_vif_to_wd + } + EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); + ++struct wireless_dev *ieee80211_vif_to_wdev_relaxed(struct ieee80211_vif *vif) ++{ ++ struct ieee80211_sub_if_data *sdata; ++ ++ if (!vif) ++ return NULL; ++ ++ sdata = vif_to_sdata(vif); ++ ++ if (sdata) ++ return &sdata->wdev; ++ ++ return NULL; ++} ++EXPORT_SYMBOL(ieee80211_vif_to_wdev_relaxed); ++ + /* + * Nothing should have been stuffed into the workqueue during + * the suspend->resume cycle. Since we can't check each caller +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -291,6 +291,17 @@ void ieee80211_link_info_change_notify(s + drv_link_info_changed(local, sdata, link->conf, link->link_id, changed); + } + ++void ieee80211_nss_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, ++ u64 changed) ++{ ++ struct ieee80211_local *local = sdata->local; ++ ++ if (!changed || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ return; ++ ++ drv_nss_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed); ++} ++ + u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) + { + sdata->vif.bss_conf.use_cts_prot = false; +@@ -691,12 +702,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_ + NL80211_FEATURE_FULL_AP_CLIENT_STATE; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA); + wiphy_ext_feature_set(wiphy, +- NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211); +- wiphy_ext_feature_set(wiphy, +- NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH); +- wiphy_ext_feature_set(wiphy, +- NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS); +- wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_SCAN_FREQ_KHZ); + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE); +@@ -1005,6 +1010,18 @@ int ieee80211_register_hw(struct ieee802 + return -EINVAL; + } + ++ /* Control port over nl80211 is disabled for nss offload as ++ * sending per packet tx status is not supported and only ++ * rx over netdev from driver is done currently */ ++ if (!ieee80211_hw_check(hw, SUPPORTS_NSS_OFFLOAD)) { ++ wiphy_ext_feature_set(hw->wiphy, ++ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211); ++ wiphy_ext_feature_set(hw->wiphy, ++ NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH); ++ wiphy_ext_feature_set(hw->wiphy, ++ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS); ++ } ++ + #ifdef CONFIG_PM + if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) + return -EINVAL; +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -2390,6 +2390,9 @@ sta_get_last_rx_stats(struct sta_info *s + struct ieee80211_sta_rx_stats *stats = &sta->deflink.rx_stats; + int cpu; + ++ if (ieee80211_hw_check(&sta->local->hw, SUPPORTS_NSS_OFFLOAD)) ++ return stats; ++ + if (!sta->deflink.pcpu_rx_stats) + return stats; + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -1029,11 +1029,23 @@ ieee80211_tx_h_stats(struct ieee80211_tx + { + struct sk_buff *skb; + int ac = -1; ++ struct ieee80211_hdr *hdr; ++ bool nss_offload; + + if (!tx->sta) + return TX_CONTINUE; + ++ nss_offload = ieee80211_hw_check(&tx->local->hw, SUPPORTS_NSS_OFFLOAD); ++ + skb_queue_walk(&tx->skbs, skb) { ++ /* Do not increment stats for data packets if NSS offload is enabled. ++ * As we use the stats from NSS, this will be a duplication ++ */ ++ if (nss_offload) { ++ hdr = (void *) skb->data; ++ if (ieee80211_is_data(hdr->frame_control)) ++ continue; ++ } + ac = skb_get_queue_mapping(skb); + tx->sta->deflink.tx_stats.bytes[ac] += skb->len; + } +@@ -2858,7 +2870,9 @@ static struct sk_buff *ieee80211_build_h + + if (unlikely(!multicast && ((skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) || +- ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS))) ++ ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS) && ++ !(ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && ++ ieee80211_is_data(fc) && !ieee80211_is_qos_nullfunc(fc)))) + info_id = ieee80211_store_ack_skb(local, skb, &info_flags, + cookie); + +@@ -4642,13 +4656,16 @@ static void ieee80211_8023_xmit(struct i + } + + if (unlikely(skb->sk && +- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) ++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && ++ !ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD))) + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, + &info->flags, NULL); + + dev_sw_netstats_tx_add(dev, skbs, len); +- sta->deflink.tx_stats.packets[queue] += skbs; +- sta->deflink.tx_stats.bytes[queue] += len; ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ sta->deflink.tx_stats.packets[queue] += skbs; ++ sta->deflink.tx_stats.bytes[queue] += len; ++ } + + ieee80211_tpt_led_trig_tx(local, len); + +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -2447,6 +2447,9 @@ bool cfg80211_does_bw_fit_range(const st + + int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp) + { ++ if(sinfo->pertid) ++ return 0; ++ + sinfo->pertid = kcalloc(IEEE80211_NUM_TIDS + 1, + sizeof(*(sinfo->pertid)), + gfp); +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2678,7 +2678,7 @@ static int ieee80211_change_bss(struct w + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_link_data *link; + struct ieee80211_supported_band *sband; +- u64 changed = 0; ++ u32 changed = 0, nss_changed = 0; + + link = ieee80211_link_or_deflink(sdata, params->link_id, true); + if (IS_ERR(link)) +@@ -2728,6 +2728,8 @@ static int ieee80211_change_bss(struct w + sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS; + else + sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS; ++ sdata->vif.bss_conf.nss_ap_isolate = params->ap_isolate; ++ nss_changed |= BSS_CHANGED_NSS_AP_ISOLATE; + ieee80211_check_fast_rx_iface(sdata); + } + +@@ -2756,6 +2758,8 @@ static int ieee80211_change_bss(struct w + + ieee80211_link_info_change_notify(sdata, link, changed); + ++ ieee80211_nss_bss_info_change_notify(sdata, nss_changed); ++ + return 0; + } + +--- a/net/mac80211/driver-ops.h ++++ b/net/mac80211/driver-ops.h +@@ -172,6 +172,23 @@ void drv_link_info_changed(struct ieee80 + struct ieee80211_bss_conf *info, + int link_id, u64 changed); + ++static inline void drv_nss_bss_info_changed(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ struct ieee80211_bss_conf *info, ++ u32 changed) ++{ ++ might_sleep(); ++ ++ if (!check_sdata_in_driver(sdata)) ++ return; ++ ++ trace_drv_nss_bss_info_changed(local, sdata, info, changed); ++ if (local->ops->nss_bss_info_changed) { ++ local->ops->nss_bss_info_changed(&local->hw, &sdata->vif, changed); ++ } ++ trace_drv_nss_return_void(local); ++} ++ + static inline u64 drv_prepare_multicast(struct ieee80211_local *local, + struct netdev_hw_addr_list *mc_list) + { +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1847,6 +1847,8 @@ void ieee80211_vif_cfg_change_notify(str + void ieee80211_link_info_change_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_link_data *link, + u64 changed); ++void ieee80211_nss_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, ++ u64 changed); + void ieee80211_configure_filter(struct ieee80211_local *local); + u64 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata); + +--- a/net/mac80211/trace.h ++++ b/net/mac80211/trace.h +@@ -389,6 +389,38 @@ TRACE_EVENT(drv_config, + LOCAL_PR_ARG, __entry->changed, CHANDEF_PR_ARG + ) + ); ++TRACE_EVENT(drv_nss_bss_info_changed, ++ TP_PROTO(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ struct ieee80211_bss_conf *info, ++ u32 changed), ++ ++ TP_ARGS(local, sdata, info, changed), ++ ++ TP_STRUCT__entry( ++ LOCAL_ENTRY ++ VIF_ENTRY ++ __field(u32, changed) ++ __field(bool, nss_ap_isolate) ++ ), ++ ++ TP_fast_assign( ++ LOCAL_ASSIGN; ++ VIF_ASSIGN; ++ __entry->changed = changed; ++ __entry->nss_ap_isolate = info->nss_ap_isolate; ++ ), ++ ++ TP_printk( ++ LOCAL_PR_FMT VIF_PR_FMT " changed:%#x", ++ LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed ++ ) ++); ++ ++DEFINE_EVENT(local_only_evt, drv_nss_return_void, ++ TP_PROTO(struct ieee80211_local *local), ++ TP_ARGS(local) ++); + + TRACE_EVENT(drv_vif_cfg_changed, + TP_PROTO(struct ieee80211_local *local, +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -969,7 +969,8 @@ static bool ieee80211_set_sdata_offload_ + local->hw.wiphy->frag_threshold != (u32)-1) + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; + +- if (local->monitors) ++ if (local->monitors && ++ !ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; + } else { + flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED; +--- a/net/mac80211/Kconfig ++++ b/net/mac80211/Kconfig +@@ -104,6 +104,15 @@ menuconfig MAC80211_DEBUG_MENU + help + This option collects various mac80211 debug settings. + ++config MAC80211_NSS_SUPPORT ++ bool "QTI mac80211 nss support" ++ depends on ATH11K_NSS_SUPPORT ++ default n ++ ---help--- ++ Enables NSS offload support for ATH11K driver ++ ++ If unsure, say Y to enable NSS offload support. ++ + config MAC80211_NOINLINE + bool "Do not inline TX/RX handlers" + depends on MAC80211_DEBUG_MENU +--- a/local-symbols ++++ b/local-symbols +@@ -65,6 +65,7 @@ MAC80211_MESH_PS_DEBUG= + MAC80211_TDLS_DEBUG= + MAC80211_DEBUG_COUNTERS= + MAC80211_STA_HASH_MAX_SIZE= ++MAC80211_NSS_SUPPORT= + QCOM_AOSS_QMP= + QCOM_COMMAND_DB= + QCOM_CPR= diff --git a/package/kernel/mac80211/patches/nss/subsys/199-mac80211-fix-xmit-callback-when-hwencap-enable-in-st.patch b/package/kernel/mac80211/patches/nss/subsys/199-mac80211-fix-xmit-callback-when-hwencap-enable-in-st.patch new file mode 100644 index 00000000000000..01740e29803580 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/199-mac80211-fix-xmit-callback-when-hwencap-enable-in-st.patch @@ -0,0 +1,35 @@ +From c3389f87ea09dea804cda2483922e03ad3eb6c79 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Thu, 18 Jun 2020 00:07:15 +0530 +Subject: [PATCH] mac80211: fix xmit callback when hwencap enable in sta + +Since transmit control port uses same callback for both +(ieee80211_subif_start_xmit) ethernet mode and native +wifi mode, which cause regression in ethernet mode +when we use DUT as a STA with encryption(psk2+ccmp). + +Added hardware encap check to filter out ethernet mode +packets to follow ieee80211_subif_start_xmit_8023 callback. + +Signed-off-by: P Praneesh +--- + net/mac80211/tx.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -6216,7 +6216,13 @@ start_xmit: + mutex_lock(&local->mtx); + + local_bh_disable(); +- __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); ++ ++ /* added hardware encap check for ethernet mode */ ++ if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ++ ieee80211_subif_start_xmit_8023(skb, skb->dev); ++ else ++ __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); ++ + local_bh_enable(); + + mutex_unlock(&local->mtx); diff --git a/package/kernel/mac80211/patches/nss/subsys/203-mac80211-ath11k-fw-dynamic-muedca.patch b/package/kernel/mac80211/patches/nss/subsys/203-mac80211-ath11k-fw-dynamic-muedca.patch new file mode 100644 index 00000000000000..d4410bd712f669 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/203-mac80211-ath11k-fw-dynamic-muedca.patch @@ -0,0 +1,203 @@ +From ed838800bb8f4c59b320395066ac356f74528a50 Mon Sep 17 00:00:00 2001 +From: Muna Sinada +Date: Wed, 29 Jul 2020 00:11:30 -0700 +Subject: [PATCH] 203-mac80211-ath11k-fw-dynamic-muedca.patch + +mac80211/ath11k:FW Initiated Dynamic MU-EDCA + +Implementing the updating of firmware initiated dynamic MU-EDCA +parameters in Beacon IE. Firmware routinely checks its clients and +updates its MU-EDCA values every 3 seconds. Firmware is tuning +MU-EDCA parameters to improve performance. As part of this process, +the firmware informs host about new MU-EDCA values utilizing +WMI_MUEDCA_PARAMS_CONFIG_EVENTID. FW expectation is that host will +update MU-EDCA parameters in the Beacon IE. +Implementation consists of: + (1) Receiving updated parameters through event in ATH11k + (2) Passing updated parameters ATH11k -> mac80211 -> cfg80211 + (3) Passing updated parameters to user space. + +Signed-off-by: Muna Sinada +--- + drivers/net/wireless/ath/ath11k/wmi.c | 97 +++++++++++++++++++++++++++++++---- + drivers/net/wireless/ath/ath11k/wmi.h | 12 +++++ + include/net/cfg80211.h | 11 ++++ + include/net/mac80211.h | 13 +++++ + include/uapi/linux/nl80211.h | 10 ++++ + net/mac80211/mlme.c | 12 +++++ + net/mac80211/trace.h | 20 ++++++++ + net/wireless/nl80211.c | 36 +++++++++++++ + 8 files changed, 200 insertions(+), 11 deletions(-) + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -9327,4 +9327,15 @@ bool cfg80211_valid_disable_subchannel_b + */ + void cfg80211_links_removed(struct net_device *dev, u16 link_mask); + ++/** ++ * cfg80211_update_muedca_params_event - Notify the updated MU-EDCA parameters ++ * to user space. ++ * @wiphy: the wiphy ++ * @params: Updated MU-EDCA parameters ++ * @gfp: allocation flags ++ */ ++void cfg80211_update_muedca_params_event(struct wiphy *wiphy, ++ struct ieee80211_mu_edca_param_set ++ *params, gfp_t gfp); ++ + #endif /* __NET_CFG80211_H */ +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -7363,6 +7363,20 @@ u32 ieee80211_calc_rx_airtime(struct iee + int len); + + /** ++ * ieee80211_update_muedca_params - update MU-EDCA parameters. ++ * ++ * This function is used to pass dynamically updated MU-EDCA parameters from ++ * driver to user space in order for parameters to be updated in beacon. ++ * ++ * @hw: pointer as obtained from ieee80211_alloc_hw() ++ * @params: updated MU-EDCA paramters ++ * @gfp: allocation flags ++ */ ++void ieee80211_update_muedca_params(struct ieee80211_hw *hw, ++ struct ieee80211_mu_edca_param_set ++ *params, gfp_t gfp); ++ ++/** + * ieee80211_calc_tx_airtime - calculate estimated transmission airtime for TX. + * + * This function calculates the estimated airtime usage of a frame based on the +--- a/include/uapi/linux/nl80211.h ++++ b/include/uapi/linux/nl80211.h +@@ -1314,6 +1314,10 @@ + * Multi-Link reconfiguration. %NL80211_ATTR_MLO_LINKS is used to provide + * information about the removed STA MLD setup links. + * ++ * @NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS: Updated MU-EDCA parameters from driver. ++ * This event is used to update dynamic MU-EDCA parameters in Beacon frame, ++ * coming from driver and now need to be reflected in Beacon frame. ++ * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +@@ -1569,6 +1573,7 @@ enum nl80211_commands { + + NL80211_CMD_LINKS_REMOVED, + ++ NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS, + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ +@@ -2815,6 +2820,8 @@ enum nl80211_commands { + * @NL80211_ATTR_MLO_LINK_DISABLED: Flag attribute indicating that the link is + * disabled. + * ++ * @NL80211_ATTR_HE_MUEDCA_PARAMS: MU-EDCA AC parameters for the ++ * %NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS command. + * @NUM_NL80211_ATTR: total number of nl80211_attrs available + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use +@@ -3353,6 +3360,8 @@ enum nl80211_attrs { + + NL80211_ATTR_MLO_LINK_DISABLED, + ++ NL80211_ATTR_HE_MUEDCA_PARAMS, ++ + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -7954,3 +7954,15 @@ void ieee80211_disable_rssi_reports(stru + _ieee80211_enable_rssi_reports(sdata, 0, 0); + } + EXPORT_SYMBOL(ieee80211_disable_rssi_reports); ++ ++void ieee80211_update_muedca_params(struct ieee80211_hw *hw, ++ struct ieee80211_mu_edca_param_set ++ *params, gfp_t gfp) ++{ ++ struct ieee80211_local *local = hw_to_local(hw); ++ ++ trace_api_update_muedca_params(local, params); ++ ++ cfg80211_update_muedca_params_event(local->hw.wiphy, params, gfp); ++} ++EXPORT_SYMBOL(ieee80211_update_muedca_params); +--- a/net/mac80211/trace.h ++++ b/net/mac80211/trace.h +@@ -3092,6 +3092,26 @@ TRACE_EVENT(stop_queue, + ) + ); + ++TRACE_EVENT(api_update_muedca_params, ++ TP_PROTO(struct ieee80211_local *local, ++ struct ieee80211_mu_edca_param_set *params), ++ ++ TP_ARGS(local, params), ++ ++ TP_STRUCT__entry( ++ LOCAL_ENTRY ++ ), ++ ++ TP_fast_assign( ++ LOCAL_ASSIGN; ++ ), ++ ++ TP_printk( ++ LOCAL_PR_FMT " updated MU-EDCA parameters", ++ LOCAL_PR_ARG ++ ) ++); ++ + #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */ + + #undef TRACE_INCLUDE_PATH +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -20211,6 +20211,42 @@ nla_put_failure: + } + EXPORT_SYMBOL(cfg80211_update_owe_info_event); + ++void cfg80211_update_muedca_params_event(struct wiphy *wiphy, ++ struct ieee80211_mu_edca_param_set ++ *params, gfp_t gfp) ++{ ++ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); ++ struct sk_buff *msg; ++ void *hdr; ++ ++ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); ++ if (!msg) ++ return; ++ ++ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS); ++ if (!hdr) ++ goto nla_put_failure; ++ ++ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) ++ goto nla_put_failure; ++ ++ if (nla_put(msg, NL80211_ATTR_HE_MUEDCA_PARAMS, ++ sizeof(struct ieee80211_mu_edca_param_set), ++ (const void *)params)) ++ goto nla_put_failure; ++ ++ genlmsg_end(msg, hdr); ++ ++ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, ++ NL80211_MCGRP_MLME, gfp); ++ return; ++ ++nla_put_failure: ++ genlmsg_cancel(msg, hdr); ++ nlmsg_free(msg); ++} ++EXPORT_SYMBOL(cfg80211_update_muedca_params_event); ++ + /* initialisation/exit functions */ + + int __init nl80211_init(void) diff --git a/package/kernel/mac80211/patches/nss/subsys/207-ath11k-Add-support-for-dynamic-vlan.patch b/package/kernel/mac80211/patches/nss/subsys/207-ath11k-Add-support-for-dynamic-vlan.patch new file mode 100644 index 00000000000000..5ae1ba64bba9d5 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/207-ath11k-Add-support-for-dynamic-vlan.patch @@ -0,0 +1,60 @@ +From f013e1e9829ec346fa0a215552eef51953b46bf0 Mon Sep 17 00:00:00 2001 +From: Seevalamuthu Mariappan +Date: Fri, 7 Aug 2020 18:24:32 +0530 +Subject: [PATCH] ath11k: Add support for dynamic vlan + +This patch adds support for dynamic vlan. VLAN group traffics +are encrypted in software. vlan unicast packets shall be taking +8023 xmit path if encap offload is enabled and mcast/bcast will +be using 80211 xmit path. + +Metadata info in dp_tx added to notify firmware that the +multicast/broadcast packets are encrypted in sw. + +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Gautham Kumar Senthilkumaran +--- + net/mac80211/tx.c | 14 +++ + 1 files changed, 14 insertions(+) + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -37,6 +37,9 @@ + #include "wme.h" + #include "rate.h" + ++static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, ++ struct net_device *dev, struct sta_info *sta, ++ struct ieee80211_key *key, struct sk_buff *skb); + /* misc utils */ + + static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, +@@ -4271,6 +4274,8 @@ void __ieee80211_subif_start_xmit(struct + struct sta_info *sta; + struct sk_buff *next; + int len = skb->len; ++ struct ieee80211_key *key = NULL; ++ struct ieee80211_sub_if_data *ap_sdata; + + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { + kfree_skb(skb); +@@ -4292,6 +4297,19 @@ void __ieee80211_subif_start_xmit(struct + if (IS_ERR(sta)) + sta = NULL; + ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { ++ ap_sdata = container_of(sdata->bss, ++ struct ieee80211_sub_if_data, u.ap); ++ if (ap_sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED && ++ !is_multicast_ether_addr(skb->data)) { ++ if (sta) ++ key = rcu_dereference(sta->ptk[sta->ptk_idx]); ++ ieee80211_8023_xmit(sdata, dev, sta, key, skb); ++ rcu_read_unlock(); ++ return; ++ } ++ } ++ + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + ieee80211_aggr_check(sdata, sta, skb); + diff --git a/package/kernel/mac80211/patches/nss/subsys/207-mac80211-add-nss-redirect-support.patch b/package/kernel/mac80211/patches/nss/subsys/207-mac80211-add-nss-redirect-support.patch new file mode 100644 index 00000000000000..38d737dc37d96a --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/207-mac80211-add-nss-redirect-support.patch @@ -0,0 +1,268 @@ +From 3acca4ecfe25a7d97e7cb820fd8c7c6324a1f318 Mon Sep 17 00:00:00 2001 +From: Sowmiya Sree Elavalagan +Date: Tue, 18 Aug 2020 16:17:25 +0530 +Subject: [PATCH] ath11k : Add NSS redirect support + +Add NSS Redirect support for ath11k. Tested on ipq8074 +Most of the changes are similar to the one done for ath10k with +minor changes to send to nss with eth header + +Redirect can be enabled by setting nss_redirect module param during +mac80211 insmod followed by ecm start +insmod mac80211.ko nss_redirect=1 +/etc/init.d/qca-nss-ecm start + +Check for ipv4_hash_hits counts in ipv4 stats and eth_rx +in qca-nss-drv to check whether packets are redirected or not. + +Verified both in nwifi and ethernet mode by running tcp and udp +traffic. + +Co-developed by: Sriram R +Signed-off-by: Sriram R +Signed-off-by: Sowmiya Sree Elavalagan +--- + net/mac80211/ieee80211_i.h | 2 ++ + net/mac80211/iface.c | 43 ++++++++++++++++++++++++++++++++ + net/mac80211/rx.c | 61 +++++++++++++++++++++++++++++++++++++--------- + net/mac80211/tx.c | 32 ++++++++++++++++++++++++ + 4 files changed, 126 insertions(+), 12 deletions(-) + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -34,6 +34,9 @@ + #include "sta_info.h" + #include "debug.h" + #include "drop.h" ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++#include ++#endif + + extern const struct cfg80211_ops mac80211_config_ops; + +@@ -1120,6 +1123,9 @@ struct ieee80211_sub_if_data { + struct dentry *default_beacon_key; + } debugfs; + #endif ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ struct nss_virt_if_handle *nssctx; ++#endif + + /* must be last, dynamically sized area in this! */ + struct ieee80211_vif vif; +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -27,6 +27,12 @@ + #include "wme.h" + #include "rate.h" + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++bool nss_redirect = false; ++module_param(nss_redirect, bool, 0644); ++MODULE_PARM_DESC(nss_redirect, "module param to enable NSS Redirect; 1-enable, 0-disable"); ++#endif ++ + /** + * DOC: Interface list locking + * +@@ -752,6 +758,13 @@ static int ieee80211_stop(struct net_dev + + cancel_work_sync(&sdata->activate_links_work); + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ if (sdata->nssctx) { ++ nss_virt_if_destroy_sync(sdata->nssctx); ++ sdata_info(sdata, "Destroyed NSS virtual interface\n"); ++ } ++#endif ++ + wiphy_lock(sdata->local->hw.wiphy); + ieee80211_do_stop(sdata, true); + wiphy_unlock(sdata->local->hw.wiphy); +@@ -1219,6 +1232,34 @@ void ieee80211_del_virtual_monitor(struc + kfree(sdata); + } + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++/* This callback is registered for nss redirect to receive packet exceptioned from nss in Rx path. ++ * When packet does not match any of the ecm rules is redirected back here. ++ */ ++void receive_from_nss(struct net_device *dev, struct sk_buff *sk_buff, struct napi_struct *napi) ++{ ++ struct net_device *netdev; ++ struct sk_buff *skb; ++ struct ieee80211_sub_if_data *sdata; ++ ++ if (!dev) { ++ kfree(sk_buff); ++ return; ++ } ++ ++ netdev = (struct net_device *)dev; ++ sdata = netdev_priv(netdev); ++ if (sdata->dev != dev) { ++ kfree(sk_buff); ++ return; ++ } ++ skb = (struct sk_buff *)sk_buff; ++ skb->dev = netdev; ++ skb->protocol = eth_type_trans(skb, netdev); ++ napi_gro_receive(napi, skb); ++} ++#endif ++ + /* + * NOTE: Be very careful when changing this function, it must NOT return + * an error on interface type changes that have been pre-checked, so most +@@ -1450,6 +1491,19 @@ int ieee80211_do_open(struct wireless_de + + ieee80211_recalc_ps(local); + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ sdata->nssctx = NULL; ++ if (nss_redirect) { ++ sdata->nssctx = nss_virt_if_create_sync(dev); ++ if (sdata->nssctx) { ++ sdata_info(sdata, "Created a NSS virtual interface\n"); ++ nss_virt_if_register(sdata->nssctx, receive_from_nss, sdata->dev); ++ } else { ++ sdata_info(sdata, "Failed to create a NSS virtual interface\n"); ++ } ++ } ++#endif ++ + set_bit(SDATA_STATE_RUNNING, &sdata->state); + + return 0; +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2569,6 +2569,54 @@ static bool ieee80211_frame_allowed(stru + return true; + } + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++#define case_rtn_string(val) case val: return #val ++ ++static const char *nss_tx_status_str(nss_tx_status_t status) ++{ ++ switch (status) { ++ case_rtn_string(NSS_TX_SUCCESS); ++ case_rtn_string(NSS_TX_FAILURE); ++ case_rtn_string(NSS_TX_FAILURE_QUEUE); ++ case_rtn_string(NSS_TX_FAILURE_NOT_READY); ++ case_rtn_string(NSS_TX_FAILURE_TOO_LARGE); ++ case_rtn_string(NSS_TX_FAILURE_TOO_SHORT); ++ case_rtn_string(NSS_TX_FAILURE_NOT_SUPPORTED); ++ case_rtn_string(NSS_TX_FAILURE_BAD_PARAM); ++ default: ++ return "Unknown NSS TX status"; ++ } ++} ++ ++static void netif_rx_nss(struct ieee80211_rx_data *rx, ++ struct sk_buff *skb) ++{ ++ struct ieee80211_sub_if_data *sdata = rx->sdata; ++ int ret; ++ ++ if (!sdata->nssctx) ++ goto out; ++ ++ /* NSS expects ethernet header in skb data so resetting here */ ++ skb_push(skb, ETH_HLEN); ++ ret = nss_virt_if_tx_buf(sdata->nssctx, skb); ++ if (ret) { ++ if (net_ratelimit()) { ++ sdata_err(sdata, "NSS TX failed with error: %s\n", ++ nss_tx_status_str(ret)); ++ } ++ goto out; ++ } ++ ++ return; ++out: ++ if (rx->napi) ++ napi_gro_receive(rx->napi, skb); ++ else ++ netif_receive_skb(skb); ++} ++#endif ++ + static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb, + struct ieee80211_rx_data *rx) + { +@@ -2608,11 +2656,15 @@ static void ieee80211_deliver_skb_to_loc + !ether_addr_equal(ehdr->h_dest, sdata->vif.addr))) + ether_addr_copy(ehdr->h_dest, sdata->vif.addr); + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ netif_rx_nss(rx, skb); ++#else + /* deliver to local stack */ + if (rx->list) + list_add_tail(&skb->list, rx->list); + else + netif_receive_skb(skb); ++#endif + } + } + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4504,6 +4504,35 @@ static void ieee80211_mlo_multicast_tx(s + kfree_skb(skb); + } + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++void ieee80211_xmit_nss_fixup(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); ++ ++ /* Packets from NSS does not have valid protocol, priority and other ++ * network stack values. Derive required parameters (priority ++ * and network_header) from payload for QoS header. ++ * XXX: Here the assumption is that packet are in 802.3 format. ++ * As of now priority is handled only for IPv4 and IPv6. ++ */ ++ ++ if (sdata->nssctx && likely(!skb->protocol)) { ++ skb_set_network_header(skb, 14); ++ switch (((struct ethhdr *)skb->data)->h_proto) { ++ case htons(ETH_P_IP): ++ skb->priority = (ipv4_get_dsfield(ip_hdr(skb)) & ++ 0xfc) >> 5; ++ break; ++ case htons(ETH_P_IPV6): ++ skb->priority = (ipv6_get_dsfield(ipv6_hdr(skb)) & ++ 0xfc) >> 5; ++ break; ++ } ++ } ++} ++#endif ++ + /** + * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs + * @skb: packet to be sent +@@ -4517,6 +4546,10 @@ netdev_tx_t ieee80211_subif_start_xmit(s + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + const struct ethhdr *eth = (void *)skb->data; + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ ieee80211_xmit_nss_fixup(skb, dev); ++#endif ++ + if (likely(!is_multicast_ether_addr(eth->h_dest))) + goto normal; + +@@ -4703,6 +4736,9 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + struct ieee80211_key *key; + struct sta_info *sta; + ++#ifdef CPTCFG_MAC80211_NSS_SUPPORT ++ ieee80211_xmit_nss_fixup(skb, dev); ++#endif + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { + kfree_skb(skb); + return NETDEV_TX_OK; diff --git a/package/kernel/mac80211/patches/nss/subsys/235-001-mac80211-add-AP_VLAN-iftype-support-on-NSS-offload-case.patch b/package/kernel/mac80211/patches/nss/subsys/235-001-mac80211-add-AP_VLAN-iftype-support-on-NSS-offload-case.patch new file mode 100644 index 00000000000000..3d543d714122dd --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/235-001-mac80211-add-AP_VLAN-iftype-support-on-NSS-offload-case.patch @@ -0,0 +1,222 @@ +From 05d9bff2eb8b057d34c7c4b24329dd92cf4faddb Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Wed, 18 Nov 2020 23:54:38 +0530 +Subject: [PATCH 1/3] mac80211: add AP_VLAN iftype support on NSS offload case + +- allow AP_VLAN iftype to get added, removed +- add new callback for 4addr rx_notify to get AP_VLAN created from hostapd +- modify sta_use_4addr drv callback to advertise AP_VLAN vif instead of AP vif +- modify drv_tx callback to use AP_VLAN vif on NSS offload case + +Signed-off-by: Sathishkumar Muruganandam +--- + include/net/mac80211.h | 11 +++++++++++ + net/mac80211/cfg.c | 5 ++++- + net/mac80211/driver-ops.c | 9 +++++---- + net/mac80211/iface.c | 10 ++++++---- + net/mac80211/rx.c | 6 ++++++ + net/mac80211/tx.c | 14 ++++++++++---- + net/mac80211/util.c | 6 ++---- + 7 files changed, 44 insertions(+), 17 deletions(-) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -5100,6 +5100,17 @@ void ieee80211_sta_pspoll(struct ieee802 + */ + void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *sta, u8 tid); + ++/** ++ * ieee80211_rx_nss_notify_4addr - notify userspace about 4addr frame rx ++ * @dev: The device the frame matched to ++ * @addr: the transmitter address of 4addr sta ++ * ++ * When operating in AP mode with NSS offload enabled, this function is used ++ * to invoke cfg80211 callback to notify userspace that an associated station ++ * sent a 4addr frame. ++ */ ++void ieee80211_rx_nss_notify_4addr(struct net_device *dev, u8* sta_addr); ++ + /* + * The TX headroom reserved by mac80211 for its own tx_status functions. + * This is enough for the radiotap header. +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2180,7 +2180,13 @@ static int ieee80211_change_station(stru + + rcu_assign_pointer(vlansdata->u.vlan.sta, sta); + __ieee80211_check_fast_rx_iface(vlansdata); +- drv_sta_set_4addr(local, sta->sdata, &sta->sta, true); ++ ++ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) ++ drv_sta_set_4addr(local, vlansdata, &sta->sta, ++ true); ++ else ++ drv_sta_set_4addr(local, sta->sdata, &sta->sta, ++ true); + } + + if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +--- a/net/mac80211/driver-ops.c ++++ b/net/mac80211/driver-ops.c +@@ -59,10 +59,9 @@ int drv_add_interface(struct ieee80211_l + + might_sleep(); + +- if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || +- (sdata->vif.type == NL80211_IFTYPE_MONITOR && ++ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_MONITOR && + !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && +- !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)))) ++ !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))) + return -EINVAL; + + trace_drv_add_interface(local, sdata); +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -676,6 +676,9 @@ static void ieee80211_do_stop(struct iee + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: ++ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && ++ going_down) ++ drv_remove_interface(local, sdata); + break; + case NL80211_IFTYPE_MONITOR: + if (local->monitors == 0) +@@ -960,6 +963,7 @@ static bool ieee80211_iftype_supports_hd + switch (iftype) { + /* P2P GO and client are mapped to AP/STATION types */ + case NL80211_IFTYPE_AP: ++ case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_STATION: + return true; + default: +@@ -1014,7 +1018,8 @@ static void ieee80211_set_vif_encap_ops( + struct ieee80211_sub_if_data *bss = sdata; + bool enabled; + +- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { + if (!sdata->bss) + return; + +@@ -1359,10 +1364,17 @@ int ieee80211_do_open(struct wireless_de + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: +- /* no need to tell driver, but set carrier and chanctx */ + if (sdata->bss->active) { + ieee80211_link_vlan_copy_chanctx(&sdata->deflink); + netif_carrier_on(dev); ++ ++ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ ieee80211_set_sdata_offload_flags(sdata); ++ res = drv_add_interface(local, sdata); ++ if (res) ++ goto err_del_interface; ++ } ++ + ieee80211_set_vif_encap_ops(sdata); + } else { + netif_carrier_off(dev); +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -1656,6 +1656,12 @@ void ieee80211_sta_uapsd_trigger(struct + } + EXPORT_SYMBOL(ieee80211_sta_uapsd_trigger); + ++void ieee80211_rx_nss_notify_4addr(struct net_device *dev, u8 *sta_addr) ++{ ++ cfg80211_rx_unexpected_4addr_frame(dev, sta_addr, GFP_ATOMIC); ++} ++EXPORT_SYMBOL(ieee80211_rx_nss_notify_4addr); ++ + static ieee80211_rx_result debug_noinline + ieee80211_rx_h_uapsd_and_pspoll(struct ieee80211_rx_data *rx) + { +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4298,8 +4298,13 @@ void __ieee80211_subif_start_xmit(struct + sta = NULL; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { +- ap_sdata = container_of(sdata->bss, +- struct ieee80211_sub_if_data, u.ap); ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) ++ ap_sdata = container_of(sdata->bss, ++ struct ieee80211_sub_if_data, ++ u.ap); ++ else ++ ap_sdata = sdata; ++ + if (ap_sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED && + !is_multicast_ether_addr(skb->data)) { + if (sta) +@@ -4689,7 +4694,8 @@ static void ieee80211_8023_xmit(struct i + + info->hw_queue = sdata->vif.hw_queue[queue]; + +- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -2410,6 +2410,9 @@ static void ieee80211_assign_chanctx(str + if (!local->use_chanctx) + return; + ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ return; ++ + mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected(link->conf->chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); +@@ -2615,7 +2618,8 @@ int ieee80211_reconfig(struct ieee80211_ + } + + list_for_each_entry(sdata, &local->interfaces, list) { +- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN || ++ ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) && + sdata->vif.type != NL80211_IFTYPE_MONITOR && + ieee80211_sdata_running(sdata)) { + res = drv_add_interface(local, sdata); +@@ -2630,7 +2634,8 @@ int ieee80211_reconfig(struct ieee80211_ + if (res) { + list_for_each_entry_continue_reverse(sdata, &local->interfaces, + list) +- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN || ++ ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) && + sdata->vif.type != NL80211_IFTYPE_MONITOR && + ieee80211_sdata_running(sdata)) + drv_remove_interface(local, sdata); +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -5259,7 +5259,8 @@ static bool ieee80211_assoc_success(stru + * If we're using 4-addr mode, let the AP know that we're + * doing so, so that it can create the STA VLAN on its side + */ +- if (ifmgd->use_4addr) ++ if (ifmgd->use_4addr && ++ (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD))) + ieee80211_send_4addr_nullfunc(local, sdata); + + /* +--- a/net/mac80211/driver-ops.h ++++ b/net/mac80211/driver-ops.h +@@ -1457,7 +1457,9 @@ static inline void drv_sta_set_4addr(str + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, bool enabled) + { +- sdata = get_bss_sdata(sdata); ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) ++ sdata = get_bss_sdata(sdata); ++ + if (!check_sdata_in_driver(sdata)) + return; + diff --git a/package/kernel/mac80211/patches/nss/subsys/236-001-mac80211-add-dynamic-VLAN-support-on-NSS-offload.patch b/package/kernel/mac80211/patches/nss/subsys/236-001-mac80211-add-dynamic-VLAN-support-on-NSS-offload.patch new file mode 100644 index 00000000000000..542ab4e6a93831 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/236-001-mac80211-add-dynamic-VLAN-support-on-NSS-offload.patch @@ -0,0 +1,130 @@ +From 36ee9d37b53c933f4dd8f934f8e0273b5e901549 Mon Sep 17 00:00:00 2001 +From: Sathishkumar Muruganandam +Date: Fri, 8 Jan 2021 00:02:54 +0530 +Subject: [PATCH 1/3] mac80211: add dynamic VLAN support on NSS offload + +NSS requires dynamic AP_VLAN vif ifnum and its corresponding VLAN ID +and group key index to configure dynamic VLAN ext VDEV in NSS. + +Hence mac80211 set_key and sta_state callbacks are modified to advertise +AP_VLAN vif when NSS offload is enabled and VLAN ID provided by hostapd +in key params is stored to ieee80211_key_conf for the driver. + +Co-Developed-by: Seevalamuthu Mariappan +Signed-off-by: Seevalamuthu Mariappan +Signed-off-by: Sathishkumar Muruganandam +--- + include/net/mac80211.h | 3 +++ + net/mac80211/cfg.c | 1 + + net/mac80211/driver-ops.c | 4 +++- + net/mac80211/driver-ops.h | 4 +++- + net/mac80211/key.c | 5 ++++- + net/mac80211/tx.c | 4 ++++ + 6 files changed, 18 insertions(+), 3 deletions(-) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -2088,6 +2088,8 @@ enum ieee80211_key_flags { + * @tx_pn: PN used for TX keys, may be used by the driver as well if it + * needs to do software PN assignment by itself (e.g. due to TSO) + * @flags: key flags, see &enum ieee80211_key_flags. ++ * @vlan_id: VLAN ID corresponding to the group key. ++ * For VLAN interfaces 1-4096, 0 for non-vlan interfaces + * @keyidx: the key index (0-3) + * @keylen: key material length + * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte) +@@ -2107,6 +2109,7 @@ struct ieee80211_key_conf { + u8 hw_key_idx; + s8 keyidx; + u16 flags; ++ u16 vlan_id; + s8 link_id; + u8 keylen; + u8 key[]; +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -538,6 +538,7 @@ static int ieee80211_add_key(struct wiph + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: ++ key->conf.vlan_id = params->vlan_id; + /* Keys without a station are used for TX only */ + if (sta && test_sta_flag(sta, WLAN_STA_MFP)) + key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; +--- a/net/mac80211/driver-ops.c ++++ b/net/mac80211/driver-ops.c +@@ -116,7 +116,11 @@ int drv_sta_state(struct ieee80211_local + + might_sleep(); + +- sdata = get_bss_sdata(sdata); ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) || ++ !(old_state == IEEE80211_STA_ASSOC && ++ new_state == IEEE80211_STA_AUTHORIZED)) ++ sdata = get_bss_sdata(sdata); ++ + if (!check_sdata_in_driver(sdata)) + return -EIO; + +--- a/net/mac80211/driver-ops.h ++++ b/net/mac80211/driver-ops.h +@@ -453,7 +453,9 @@ static inline int drv_sta_add(struct iee + + might_sleep(); + +- sdata = get_bss_sdata(sdata); ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) ++ sdata = get_bss_sdata(sdata); ++ + if (!check_sdata_in_driver(sdata)) + return -EIO; + +--- a/net/mac80211/key.c ++++ b/net/mac80211/key.c +@@ -166,7 +166,8 @@ static int ieee80211_key_enable_hw_accel + if (sta && !sta->uploaded) + goto out_unsupported; + +- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !ieee80211_hw_check(&key->local->hw, SUPPORTS_NSS_OFFLOAD)) { + /* + * The driver doesn't know anything about VLAN interfaces. + * Hence, don't send GTKs for VLAN interfaces to the driver. +@@ -610,6 +611,8 @@ ieee80211_key_alloc(u32 cipher, int idx, + */ + key->conf.flags = 0; + key->flags = 0; ++ /* VLAN ID initialised to zero for non-vlan interfaces */ ++ key->conf.vlan_id = 0; + + key->conf.link_id = -1; + key->conf.cipher = cipher; +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4650,16 +4650,25 @@ static void ieee80211_8023_xmit(struct i + struct ieee80211_key *key, struct sk_buff *skb) + { + struct ieee80211_tx_info *info; ++ struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_local *local = sdata->local; + struct tid_ampdu_tx *tid_tx; + struct sk_buff *seg, *next; + unsigned int skbs = 0, len = 0; + u16 queue; ++ unsigned char *ra = ehdr->h_dest; ++ bool multicast; + u8 tid; + + queue = ieee80211_select_queue(sdata, sta, skb); + skb_set_queue_mapping(skb, queue); + ++ multicast = is_multicast_ether_addr(ra); ++ ++ if (multicast && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && ++ !atomic_read(&sdata->u.vlan.num_mcast_sta)) ++ goto out_free; ++ + if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && + test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) + goto out_free; diff --git a/package/kernel/mac80211/patches/nss/subsys/245-compilation_fix.patch b/package/kernel/mac80211/patches/nss/subsys/245-compilation_fix.patch new file mode 100644 index 00000000000000..0e92c0f1332b7d --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/245-compilation_fix.patch @@ -0,0 +1,115 @@ +From 9cdb8bae50aca80b593d0f53be5b8efedfc91324 Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam +Date: Sun, 7 Mar 2021 22:49:26 +0530 +Subject: [PATCH] backport: Compile fix + +Adding these changes to fix compilation issue due to +package upgrade + +Signed-off-by: Tamizh Chelvam +Signed-off-by: Gautham Kumar Senthilkumaran +--- + include/linux/backport-refcount.h | 4 +-- + include/net/fq.h | 10 +++++- + net/mac80211/cfg.c | 4 +-- + net/mac80211/ieee80211_i.h | 4 ++- + net/mac80211/iface.c | 2 -- + net/mac80211/rx.c | 23 +++++++++----- + net/mac80211/tx.c | 54 ++++++++++++++++++++++---------- + 7 files changed, 40 insertions(+), 23 deletions(-) + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -207,6 +207,7 @@ enum ieee80211_rx_flags { + }; + + struct ieee80211_rx_data { ++ struct napi_struct *napi; + struct list_head *list; + struct sk_buff *skb; + struct ieee80211_local *local; +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4679,19 +4679,21 @@ static void ieee80211_8023_xmit(struct i + + ieee80211_aggr_check(sdata, sta, skb); + +- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; +- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); +- if (tid_tx) { +- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { +- /* fall back to non-offload slow path */ +- __ieee80211_subif_start_xmit(skb, dev, 0, +- IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, +- NULL); +- return; +- } ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; ++ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); ++ if (tid_tx) { ++ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { ++ /* fall back to non-offload slow path */ ++ __ieee80211_subif_start_xmit(skb, dev, 0, ++ IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, ++ NULL); ++ return; ++ } + +- if (tid_tx->timeout) +- tid_tx->last_tx = jiffies; ++ if (tid_tx->timeout) ++ tid_tx->last_tx = jiffies; ++ } + } + + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); +@@ -4748,7 +4750,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + { + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; +- struct ieee80211_key *key; ++ struct ieee80211_key *key = NULL; + struct sta_info *sta; + + #ifdef CPTCFG_MAC80211_NSS_SUPPORT +@@ -4766,9 +4768,13 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + goto out; + } + +- if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || +- !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || +- sdata->control_port_protocol == ehdr->h_proto)) ++ if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded)) ++ sta = NULL; ++ goto tx_offload; ++ } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || ++ !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || ++ sdata->control_port_protocol == ehdr->h_proto)) + goto skip_offload; + + key = rcu_dereference(sta->ptk[sta->ptk_idx]); +@@ -4779,6 +4785,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + goto skip_offload; + + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); ++tx_offload: + ieee80211_8023_xmit(sdata, dev, sta, key, skb); + goto out; + +@@ -6285,13 +6292,7 @@ start_xmit: + mutex_lock(&local->mtx); + + local_bh_disable(); +- +- /* added hardware encap check for ethernet mode */ +- if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) +- ieee80211_subif_start_xmit_8023(skb, skb->dev); +- else +- __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); +- ++ __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); + local_bh_enable(); + + mutex_unlock(&local->mtx); diff --git a/package/kernel/mac80211/patches/nss/subsys/300-ath11k-nss-mesh-offload-support.patch b/package/kernel/mac80211/patches/nss/subsys/300-ath11k-nss-mesh-offload-support.patch new file mode 100644 index 00000000000000..e1b46836c6b4c5 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/300-ath11k-nss-mesh-offload-support.patch @@ -0,0 +1,1250 @@ +From fbe5a76d8c9ff1cf3f906a3c863928fc1adcbc95 Mon Sep 17 00:00:00 2001 +From: Karthikeyan Kathirvel +Date: Tue, 16 Feb 2021 13:44:39 +0530 +Subject: [PATCH] ath11k: Add mesh nss offload support + +- New capability advertising nss offload support for mesh type +- Mesh obj vap and link vap registration/clean up +- Command/event handling +- New .ch files in ath11k for nss mesh offload related debugs +- Tx/Rx data path on mesh link vap uses native wifi format +- Mesh obj vap handls packets in ether format. No Tx on Mesh + obj vap is expected as packets transmitted in slow path is + supposed to be encapsulated in 802.11 format. +- New mac80211-driver callbacks for mesh vap, mpath and mpp + configurations. + +Signed-off-by: Vasanthakumar Thiagarajan + +Change-Id: Ib6950344286ba18fab43586262c62dcd09557614 +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Vasanthakumar Thiagarajan +Signed-off-by: Gautham Kumar Senthilkumaran +--- + include/net/mac80211.h | 106 ++ + net/mac80211/cfg.c | 19 +- + net/mac80211/debug.h | 10 + + net/mac80211/debugfs.c | 1 + + net/mac80211/driver-ops.c | 20 + + net/mac80211/driver-ops.h | 7 + + net/mac80211/mesh.h | 5 + + net/mac80211/mesh_hwmp.c | 273 +++++ + net/mac80211/mesh_pathtbl.c | 167 ++- + net/mac80211/rx.c | 8 +- + 10 files changed, 2548 insertions(+), 206 deletions(-) + create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.c + create mode 100644 drivers/net/wireless/ath/ath11k/debug_nss.h + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -145,6 +145,9 @@ + */ + struct device; + ++struct ieee80211_mesh_path_offld; ++enum ieee80211_mesh_path_offld_cmd; ++ + /** + * enum ieee80211_max_queues - maximum number of queues + * +@@ -387,11 +390,17 @@ enum ieee80211_bss_change { + * to indicate which NSS BSS parameter changed. + * + * @BSS_CHANGED_NSS_AP_ISOLATE: AP Isolate feature in NSS mode ++ * @BSS_CHANGED_NSS_MESH_TTL: TTL update in NSS mesh mode ++ * @BSS_CHANGED_NSS_MESH_REFRESH_TIME: Mesh refresh time in NSS mesh mode ++ * @BSS_CHANGED_NSS_MESH_FWD_ENABLED: NSS offload mesh forward enabled + * + */ + + enum ieee80211_nss_bss_change { + BSS_CHANGED_NSS_AP_ISOLATE = BIT(0), ++ BSS_CHANGED_NSS_MESH_TTL = BIT(1), ++ BSS_CHANGED_NSS_MESH_REFRESH_TIME = BIT(2), ++ BSS_CHANGED_NSS_MESH_FWD_ENABLED = BIT(3), + }; + + /* +@@ -790,6 +799,11 @@ struct ieee80211_bss_conf { + bool he_full_ul_mumimo; + bool eht_su_beamformer; + bool eht_su_beamformee; ++ ++ /* Mesh configuration for nss offload */ ++ u8 nss_offld_ttl; ++ bool nss_offld_mesh_forward_enabled; ++ u32 nss_offld_mpath_refresh_time; + bool eht_mu_beamformer; + bool nss_ap_isolate; + }; +@@ -1275,6 +1289,8 @@ struct ieee80211_rate_status { + * @ack_hwtstamp: Hardware timestamp of the received ack in nanoseconds + * Only needed for Timing measurement and Fine timing measurement action + * frames. Only reported by devices that have timestamping enabled. ++ * @mpdu_succ: Number of mpdus successfully transmitted ++ * @mpdu_fail: Number of mpdus failed + */ + struct ieee80211_tx_status { + struct ieee80211_sta *sta; +@@ -1285,6 +1301,8 @@ struct ieee80211_tx_status { + u8 n_rates; + + struct list_head *free_list; ++ u32 mpdu_succ; ++ u32 mpdu_fail; + }; + + /** +@@ -1777,6 +1795,7 @@ struct ieee80211_channel_switch { + * this is not pure P2P vif. + * @IEEE80211_VIF_DISABLE_SMPS_OVERRIDE: disable user configuration of + * SMPS mode via debugfs. ++ * @IEEE80211_HW_NSS_OFFLOAD_DEBUG_MODE: It enables the debug mode of nss offload. + */ + enum ieee80211_vif_flags { + IEEE80211_VIF_BEACON_FILTER = BIT(0), +@@ -1784,6 +1803,7 @@ enum ieee80211_vif_flags { + IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2), + IEEE80211_VIF_GET_NOA_UPDATE = BIT(3), + IEEE80211_VIF_DISABLE_SMPS_OVERRIDE = BIT(4), ++ IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE = BIT(5), + }; + + +@@ -2771,6 +2791,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_DETECTS_COLOR_COLLISION, + IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX, + IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, ++ IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +@@ -4271,6 +4292,8 @@ struct ieee80211_prep_tx_info { + * @set_sar_specs: Update the SAR (TX power) settings. + * @sta_set_decap_offload: Called to notify the driver when a station is allowed + * to use rx decapsulation offload ++ * @config_mesh_offload_path: Configure mesh path table when driver supports mesh offload. ++ * This calback must be atomic. + * @add_twt_setup: Update hw with TWT agreement parameters received from the peer. + * This callback allows the hw to check if requested parameters + * are supported and if there is enough room for a new agreement. +@@ -4654,6 +4677,12 @@ struct ieee80211_ops { + void (*sta_set_decap_offload)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enabled); ++#ifdef CPTCFG_MAC80211_MESH ++ void (*config_mesh_offload_path)(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path); ++#endif + void (*add_twt_setup)(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt); +@@ -7512,4 +7541,100 @@ int ieee80211_set_active_links(struct ie + void ieee80211_set_active_links_async(struct ieee80211_vif *vif, + u16 active_links); + ++/* Defines for Mesh NSS offload */ ++ ++enum ieee80211_mesh_path_offld_cmd { ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH, ++ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH, ++ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH, ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP, ++ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP, ++ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP, ++}; ++ ++enum ieee80211_mesh_path_offld_action { ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH = BIT(0), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL = BIT(1), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP = BIT(2), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN = BIT(3), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD = BIT(4), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE = BIT(5), ++ IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND = BIT(6), ++}; ++ ++/* Duplicate defines to make it available to driver */ ++enum ieee80211_mesh_path_flags { ++ IEEE80211_MESH_PATH_ACTIVE = BIT(0), ++ IEEE80211_MESH_PATH_RESOLVING = BIT(1), ++ IEEE80211_MESH_PATH_SN_VALID = BIT(2), ++ IEEE80211_MESH_PATH_FIXED = BIT(3), ++ IEEE80211_MESH_PATH_RESOLVED = BIT(4), ++ IEEE80211_MESH_PATH_REQ_QUEUED = BIT(5), ++ IEEE80211_MESH_PATH_DELETED = BIT(6), ++}; ++ ++struct ieee80211_mesh_path_offld { ++ u8 mesh_da[ETH_ALEN]; ++ u8 da[ETH_ALEN]; ++ u8 next_hop[ETH_ALEN]; ++ u8 old_next_hop[ETH_ALEN]; ++ u8 ta[ETH_ALEN]; ++ u32 metric; ++ unsigned long exp_time; ++ u8 hop_count; ++ u8 flags; /* See &enum ieee80211_mesh_path_flags */ ++ u8 mesh_gate; ++ u8 block_mesh_fwd; ++ u8 metadata_type; ++}; ++ ++#ifdef CPTCFG_MAC80211_MESH ++/** ieee80211_mesh_path_offld_change_notify - Notify mesh path change event. ++ * @vif: Mesh interface on which the event is being reported. ++ * @path: Mesh path which got changed. Please note not all the entries in the ++ * path will have valid information. Based on the action code, it will be ++ * processed. ++ * @action: Type of the event. ++ */ ++int ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif, ++ struct ieee80211_mesh_path_offld *path, ++ enum ieee80211_mesh_path_offld_action action); ++ ++/** ieee80211s_update_metric_ppdu - Upate tx PPDU stats for 11s metric computation ++ * ++ * @hw: the hardware the frame was transmitted by ++ * @st: tx status information ++*/ ++void ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw, ++ struct ieee80211_tx_status *st); ++ ++/** mesh_nss_offld_proxy_path_exp_update - update the expiry time from nss ++ * @vif Mesh interface on which the event is being reported. ++ * @mac: dest_mac_addr of the mesh proxy path ++ * @time_diff: This is the time diff since the mesh peer is active ++ */ ++void mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da, ++ u8* mesh_da, u32 time_diff); ++#else ++static inline int ++ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif, ++ struct ieee80211_mesh_path_offld *path, ++ enum ieee80211_mesh_path_offld_action action) ++{ ++ return 0; ++} ++ ++static inline void ++ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw, ++ struct ieee80211_tx_status *st) ++{ ++} ++ ++static inline void ++mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da, ++ u8* mesh_da, u32 time_diff) ++{ ++} ++#endif ++ + #endif /* MAC80211_H */ +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2522,6 +2522,7 @@ static int ieee80211_update_mesh_config( + struct mesh_config *conf; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_mesh *ifmsh; ++ u32 nss_changed = 0; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ifmsh = &sdata->u.mesh; +@@ -2538,8 +2539,11 @@ static int ieee80211_update_mesh_config( + conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks; + if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask)) + conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries; +- if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask)) ++ if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask)) { + conf->dot11MeshTTL = nconf->dot11MeshTTL; ++ sdata->vif.bss_conf.nss_offld_ttl = nconf->dot11MeshTTL; ++ nss_changed |= BSS_CHANGED_NSS_MESH_TTL; ++ } + if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask)) + conf->element_ttl = nconf->element_ttl; + if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) { +@@ -2553,8 +2557,12 @@ static int ieee80211_update_mesh_config( + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask)) + conf->dot11MeshHWMPmaxPREQretries = + nconf->dot11MeshHWMPmaxPREQretries; +- if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask)) ++ if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask)) { + conf->path_refresh_time = nconf->path_refresh_time; ++ sdata->vif.bss_conf.nss_offld_mpath_refresh_time = ++ nconf->path_refresh_time; ++ nss_changed |= BSS_CHANGED_NSS_MESH_REFRESH_TIME; ++ } + if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask)) + conf->min_discovery_timeout = nconf->min_discovery_timeout; + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask)) +@@ -2589,8 +2597,12 @@ static int ieee80211_update_mesh_config( + if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask)) + conf->dot11MeshHWMPRannInterval = + nconf->dot11MeshHWMPRannInterval; +- if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) ++ if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask)) { + conf->dot11MeshForwarding = nconf->dot11MeshForwarding; ++ sdata->vif.bss_conf.nss_offld_mesh_forward_enabled = ++ nconf->dot11MeshForwarding; ++ nss_changed |= BSS_CHANGED_NSS_MESH_FWD_ENABLED; ++ } + if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) { + /* our RSSI threshold implementation is supported only for + * devices that report signal in dBm. +@@ -2632,6 +2644,7 @@ static int ieee80211_update_mesh_config( + conf->dot11MeshConnectedToAuthServer = + nconf->dot11MeshConnectedToAuthServer; + ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON); ++ ieee80211_nss_bss_info_change_notify(sdata, nss_changed); + return 0; + } + +--- a/net/mac80211/debug.h ++++ b/net/mac80211/debug.h +@@ -67,6 +67,12 @@ + #define MAC80211_MESH_PS_DEBUG 0 + #endif + ++#ifdef CPTCFG_MAC80211_MESH_OFFLOAD_DEBUG ++#define MAC80211_MESH_OFFLOAD_DEBUG 1 ++#else ++#define MAC80211_MESH_OFFLOAD_DEBUG 0 ++#endif ++ + #ifdef CPTCFG_MAC80211_TDLS_DEBUG + #define MAC80211_TDLS_DEBUG 1 + #else +@@ -215,6 +221,10 @@ do { \ + _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + ++#define moffld_dbg(sdata, fmt, ...) \ ++ _sdata_dbg(MAC80211_MESH_OFFLOAD_DEBUG, \ ++ sdata, fmt, ##__VA_ARGS__) ++ + #define tdls_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_TDLS_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -506,6 +506,7 @@ static const char *hw_flag_names[] = { + FLAG(DETECTS_COLOR_COLLISION), + FLAG(MLO_MCAST_MULTI_LINK_TX), + FLAG(SUPPORTS_NSS_OFFLOAD), ++ FLAG(SUPPORTS_MESH_NSS_OFFLOAD), + #undef FLAG + }; + +--- a/net/mac80211/driver-ops.c ++++ b/net/mac80211/driver-ops.c +@@ -579,3 +579,23 @@ int drv_change_sta_links(struct ieee8021 + + return 0; + } ++ ++#ifdef CPTCFG_MAC80211_MESH ++void drv_config_mesh_offload_path(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path) ++{ ++ if (!check_sdata_in_driver(sdata)) ++ return; ++ ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_MESH_NSS_OFFLOAD)) ++ return; ++ ++ if (local->ops->config_mesh_offload_path) ++ local->ops->config_mesh_offload_path(&local->hw, ++ &sdata->vif, cmd, path); ++ ++ /* TODO: trace event */ ++} ++#endif +--- a/net/mac80211/driver-ops.h ++++ b/net/mac80211/driver-ops.h +@@ -1571,4 +1571,10 @@ int drv_change_sta_links(struct ieee8021 + struct ieee80211_sta *sta, + u16 old_links, u16 new_links); + ++#ifdef CPTCFG_MAC80211_MESH ++void drv_config_mesh_offload_path(struct ieee80211_local *local, ++ struct ieee80211_sub_if_data *sdata, ++ enum ieee80211_mesh_path_offld_cmd cmd, ++ struct ieee80211_mesh_path_offld *path); ++#endif /* CPTCFG_MAC80211_MESH */ + #endif /* __MAC80211_DRIVER_OPS */ +--- a/net/mac80211/mesh.h ++++ b/net/mac80211/mesh.h +@@ -320,6 +320,10 @@ void mesh_rx_path_sel_frame(struct ieee8 + struct ieee80211_mgmt *mgmt, size_t len); + struct mesh_path * + mesh_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst); ++struct mesh_path *__mesh_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst); ++int __mpp_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst, const u8 *mpp); + + int mesh_path_add_gate(struct mesh_path *mpath); + int mesh_path_send_to_gates(struct mesh_path *mpath); +@@ -361,6 +365,7 @@ void mesh_path_discard_frame(struct ieee + void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); + + bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); ++void mesh_nss_offld_path_update(struct mesh_path *mpath, bool is_mpath, u8 *old_next_hop_addr); + struct ieee80211_mesh_fast_tx * + mesh_fast_tx_get(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mesh_fast_tx_key *key); +--- a/net/mac80211/mesh_hwmp.c ++++ b/net/mac80211/mesh_hwmp.c +@@ -365,6 +365,13 @@ u32 airtime_link_metric_get(struct ieee8 + return (u32)result; + } + ++static inline struct sta_info * ++next_hop_deref_protected(struct mesh_path *mpath) ++{ ++ return rcu_dereference_protected(mpath->next_hop, ++ lockdep_is_held(&mpath->state_lock)); ++} ++ + /** + * hwmp_route_info_get - Update routing info to originator and transmitter + * +@@ -388,9 +395,10 @@ static u32 hwmp_route_info_get(struct ie + { + struct ieee80211_local *local = sdata->local; + struct mesh_path *mpath; +- struct sta_info *sta; ++ struct sta_info *sta, *next_hop; + bool fresh_info; + const u8 *orig_addr, *ta; ++ u8 old_next_hop_addr[ETH_ALEN] = {0}; + u32 orig_sn, orig_metric; + unsigned long orig_lifetime, exp_time; + u32 last_hop_metric, new_metric; +@@ -492,7 +500,10 @@ static u32 hwmp_route_info_get(struct ie + } + + if (fresh_info) { +- if (rcu_access_pointer(mpath->next_hop) != sta) { ++ next_hop = rcu_dereference(mpath->next_hop); ++ if (next_hop) ++ ether_addr_copy(old_next_hop_addr, next_hop->sta.addr); ++ if (next_hop != sta) { + mpath->path_change_count++; + flush_mpath = true; + } +@@ -514,6 +525,8 @@ static u32 hwmp_route_info_get(struct ie + /* draft says preq_id should be saved to, but there does + * not seem to be any use for it, skipping by now + */ ++ ++ mesh_nss_offld_path_update(mpath, true, old_next_hop_addr); + } else + spin_unlock_bh(&mpath->state_lock); + } +@@ -544,7 +557,14 @@ static u32 hwmp_route_info_get(struct ie + } + + if (fresh_info) { +- if (rcu_access_pointer(mpath->next_hop) != sta) { ++ /* Reset the old_next_hop_addr since this may have filled ++ * if orig_addr and ta are different ++ */ ++ memset(old_next_hop_addr, 0, ETH_ALEN); ++ next_hop = rcu_dereference(mpath->next_hop); ++ if (next_hop) ++ ether_addr_copy(old_next_hop_addr, next_hop->sta.addr); ++ if (next_hop != sta) { + mpath->path_change_count++; + flush_mpath = true; + } +@@ -561,6 +581,8 @@ static u32 hwmp_route_info_get(struct ie + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); + mesh_path_tx_pending(mpath); ++ ++ mesh_nss_offld_path_update(mpath, true, old_next_hop_addr); + } else + spin_unlock_bh(&mpath->state_lock); + } +@@ -697,15 +719,6 @@ static void hwmp_preq_frame_process(stru + } + } + +- +-static inline struct sta_info * +-next_hop_deref_protected(struct mesh_path *mpath) +-{ +- return rcu_dereference_protected(mpath->next_hop, +- lockdep_is_held(&mpath->state_lock)); +-} +- +- + static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + const u8 *prep_elem, u32 metric) +@@ -1349,3 +1362,274 @@ void mesh_path_tx_root_frame(struct ieee + return; + } + } ++ ++static int mesh_path_offld_mpath_refresh(struct ieee80211_sub_if_data *sdata, ++ u8 *mda) ++{ ++ struct mesh_path *mpath; ++ ++ rcu_read_lock(); ++ ++ mpath = mesh_path_lookup(sdata, mda); ++ if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) { ++ moffld_dbg(sdata, ++ "mpath lookup failed during path refresh for %pM, is_mpath %d\n", ++ mda, mpath != NULL); ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ if (!(mpath->flags & MESH_PATH_RESOLVING) && !(mpath->flags & MESH_PATH_FIXED)) ++ mesh_queue_preq(mpath, PREQ_Q_F_START | PREQ_Q_F_REFRESH); ++ ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static int mesh_path_offld_mpath_del(struct ieee80211_sub_if_data *sdata, u8 *da) ++{ ++ struct mesh_path *mpath, *mppath; ++ ++ rcu_read_lock(); ++ ++ mpath = mesh_path_lookup(sdata, da); ++ if (!mpath) { ++ moffld_dbg(sdata, "mpath lookup failed for %pM during duplicate mpath removal\n", ++ da); ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ mppath = mpp_path_lookup(sdata, da); ++ if (!mppath) { ++ moffld_dbg(sdata, "proxy path lookup failed for %pM during duplicate mpath removal\n", ++ da); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ mesh_path_del(sdata, mpath->dst); ++ ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static int mesh_path_offld_mpath_exp(struct ieee80211_sub_if_data *sdata, u8 *mda) ++{ ++ struct mesh_path *mpath; ++ ++ rcu_read_lock(); ++ ++ mpath = mesh_path_lookup(sdata, mda); ++ if (!mpath) { ++ mpath = mesh_path_add(sdata, mda); ++ if (IS_ERR(mpath)) { ++ rcu_read_unlock(); ++ moffld_dbg(sdata, ++ "failed to add mpath for %pM during mpath exp\n", mda); ++ return PTR_ERR(mpath); ++ } ++ } ++ ++ spin_lock_bh(&mpath->state_lock); ++ mpath->flags &= ~MESH_PATH_ACTIVE; ++ spin_unlock_bh(&mpath->state_lock); ++ ++ if (!(mpath->flags & MESH_PATH_RESOLVING) && ++ mesh_path_sel_is_hwmp(sdata)) ++ mesh_queue_preq(mpath, PREQ_Q_F_START); ++ ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static int mesh_path_offld_mpp_learn(struct ieee80211_sub_if_data *sdata, ++ u8 *da, u8 *mda) ++{ ++ struct mesh_path *mppath; ++ int ret; ++ ++ rcu_read_lock(); ++ mppath = mpp_path_lookup(sdata, da); ++ if (mppath) { ++ moffld_dbg(sdata, "proxy path for da %pM mesh_da %pM already exists\n", ++ da, mda); ++ rcu_read_unlock(); ++ return -EEXIST; ++ } ++ ++ ret = mpp_path_add(sdata, da, mda); ++ if (ret) ++ moffld_dbg(sdata, "failed to add proxy path entry (%d): da %pM mesh_da %pM\n", ++ ret, da, mda); ++ ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ ++static int mesh_path_offld_mpp_add(struct ieee80211_sub_if_data *sdata, ++ u8 *da, u8 *mda) ++{ ++ struct mesh_path *mppath; ++ int ret; ++ ++ rcu_read_lock(); ++ mppath = mpp_path_lookup(sdata, da); ++ if (mppath) { ++ moffld_dbg(sdata, "proxy path for da %pM mesh_da %pM already exists\n", ++ da, mda); ++ rcu_read_unlock(); ++ return -EEXIST; ++ } ++ ++ ret = __mpp_path_add(sdata, da, mda); ++ if (ret) ++ moffld_dbg(sdata, "failed to add proxy path entry (%d): da %pM mesh_da %pM\n", ++ ret, da, mda); ++ ++ rcu_read_unlock(); ++ ++ return ret; ++} ++ ++static int mesh_path_offld_mpp_update(struct ieee80211_sub_if_data *sdata, ++ u8 *da, u8 *mda) ++{ ++ struct mesh_path *mppath; ++ ++ rcu_read_lock(); ++ mppath = mpp_path_lookup(sdata, da); ++ if (!mppath) { ++ moffld_dbg(sdata, ++ "proxy path lookup for da %pM failed during MPP update with mesh_da %pM\n", ++ da, mda); ++ rcu_read_unlock(); ++ return -ENOENT; ++ } else { ++ spin_lock_bh(&mppath->state_lock); ++ if (!ether_addr_equal(mppath->mpp, mda)) ++ memcpy(mppath->mpp, mda, ETH_ALEN); ++ mppath->exp_time = jiffies; ++ spin_unlock_bh(&mppath->state_lock); ++ } ++ rcu_read_unlock(); ++ ++ return 0; ++} ++ ++static int mesh_path_offld_mpath_not_found(struct ieee80211_sub_if_data *sdata, ++ u8 *mda, u8 *ta) ++{ ++ struct mesh_path *mpath; ++ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; ++ ++ rcu_read_lock(); ++ ++ mpath = mesh_path_lookup(sdata, mda); ++ if (!mpath) { ++ mpath = mesh_path_add(sdata, mda); ++ if (IS_ERR(mpath)) { ++ moffld_dbg(sdata, "mpath add failed for mesh_da %pM (%lu)\n", ++ mda, PTR_ERR(mpath)); ++ rcu_read_unlock(); ++ return PTR_ERR(mpath); ++ } ++ } ++ ++ if (!(mpath->flags & MESH_PATH_RESOLVING) && ++ mesh_path_sel_is_hwmp(sdata)) ++ mesh_queue_preq(mpath, PREQ_Q_F_START); ++ ++ rcu_read_unlock(); ++ ++ if (!is_zero_ether_addr(ta)) ++ mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl, ++ mda, 0, WLAN_REASON_MESH_PATH_NOFORWARD, ta); ++ ++ return 0; ++} ++ ++void ieee80211s_update_metric_ppdu(struct ieee80211_hw *hw, ++ struct ieee80211_tx_status *st) ++{ ++ struct sta_info *sta; ++ int i, num_mpdu; ++ bool failed; ++ struct rate_info rinfo; ++ ++ if (!st->sta) ++ return; ++ ++ if (st->mpdu_succ) { ++ num_mpdu = st->mpdu_succ; ++ failed = false; ++ } else if (st->mpdu_fail) { ++ num_mpdu = st->mpdu_fail; ++ failed = true; ++ } else ++ return; ++ ++ sta = container_of(st->sta, struct sta_info, sta); ++ if (!ieee80211_vif_is_mesh(&sta->sdata->vif)) ++ return; ++ ++ for (i = 0; i < num_mpdu; i++) { ++ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); ++ if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > ++ LINK_FAIL_THRESH) ++ mesh_plink_broken(sta); ++ ++ if (!st->rates) ++ continue; ++ ++ rinfo = st->rates->rate_idx; ++ ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, ++ cfg80211_calculate_bitrate(&rinfo)); ++ } ++} ++EXPORT_SYMBOL(ieee80211s_update_metric_ppdu); ++ ++int ieee80211_mesh_path_offld_change_notify(struct ieee80211_vif *vif, ++ struct ieee80211_mesh_path_offld *path, ++ enum ieee80211_mesh_path_offld_action action) ++{ ++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ++ int ret = -ENOTSUPP; ++ ++ moffld_dbg(sdata, "received mesh offload event %d\n", action); ++ ++ switch (action) { ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_REFRESH: ++ ret = mesh_path_offld_mpath_refresh(sdata, path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_DEL: ++ ret = mesh_path_offld_mpath_del(sdata, path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPATH_EXP: ++ ret = mesh_path_offld_mpath_exp(sdata, path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_LEARN: ++ ret = mesh_path_offld_mpp_learn(sdata, path->da, path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_ADD: ++ ret = mesh_path_offld_mpp_add(sdata, path->da, path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_MPP_UPDATE: ++ ret = mesh_path_offld_mpp_update(sdata, path->da, ++ path->mesh_da); ++ break; ++ case IEEE80211_MESH_PATH_OFFLD_ACTION_PATH_NOT_FOUND: ++ ret = mesh_path_offld_mpath_not_found(sdata, path->da, ++ path->ta); ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(ieee80211_mesh_path_offld_change_notify); +--- a/net/mac80211/mesh_pathtbl.c ++++ b/net/mac80211/mesh_pathtbl.c +@@ -15,6 +15,7 @@ + #include "ieee80211_i.h" + #include "mesh.h" + #include ++#include "driver-ops.h" + + static void mesh_path_free_rcu(struct mesh_table *tbl, struct mesh_path *mpath); + +@@ -103,6 +104,63 @@ static void mesh_table_free(struct mesh_ + mesh_path_rht_free, tbl); + } + ++void mesh_nss_offld_proxy_path_exp_update(struct ieee80211_vif *vif, u8* da, u8* mesh_da, u32 inactive_time) ++{ ++ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ++ struct mesh_table *tbl = &sdata->u.mesh.mpp_paths; ++ struct mesh_path *mppath; ++ struct hlist_node *n; ++ unsigned long expiry; ++ ++ spin_lock_bh(&tbl->walk_lock); ++ hlist_for_each_entry_safe(mppath, n, &tbl->walk_head, walk_list) { ++ if(!ether_addr_equal(da, mppath->dst) || !ether_addr_equal(mesh_da, mppath->mpp)) ++ continue; ++ if ((!(mppath->flags & MESH_PATH_RESOLVING)) && ++ (!(mppath->flags & MESH_PATH_FIXED))) { ++ expiry = jiffies - msecs_to_jiffies(inactive_time); ++ mppath->exp_time = time_after(mppath->exp_time, expiry) ? ++ mppath->exp_time : expiry; ++ } ++ } ++ spin_unlock_bh(&tbl->walk_lock); ++} ++EXPORT_SYMBOL(mesh_nss_offld_proxy_path_exp_update); ++ ++void mesh_nss_offld_path_update(struct mesh_path *mpath, bool is_mpath, u8 *old_next_hop_addr) ++{ ++ struct ieee80211_mesh_path_offld path = {0}; ++ struct sta_info *next_hop; ++ struct ieee80211_sub_if_data *sdata = mpath->sdata; ++ ++ ++ path.metric = mpath->metric; ++ if (time_before(jiffies, mpath->exp_time)) ++ path.exp_time = jiffies_to_msecs(mpath->exp_time - jiffies); ++ ++ path.hop_count = mpath->hop_count; ++ path.flags = mpath->flags; ++ path.mesh_gate = mpath->is_gate; ++ if (is_mpath) { ++ ether_addr_copy(path.mesh_da, mpath->dst); ++ } else { ++ ether_addr_copy(path.mesh_da, mpath->mpp); ++ ether_addr_copy(path.da, mpath->dst); ++ } ++ ++ next_hop = rcu_dereference(mpath->next_hop); ++ if (next_hop) ++ ether_addr_copy(path.next_hop, next_hop->addr); ++ ++ if (old_next_hop_addr) ++ ether_addr_copy(path.old_next_hop, old_next_hop_addr); ++ ++ drv_config_mesh_offload_path(sdata->local, sdata, ++ is_mpath ? IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPATH : ++ IEEE80211_MESH_PATH_OFFLD_CMD_UPDATE_MPP, ++ &path); ++} ++ + /** + * mesh_path_assign_nexthop - update mesh path next hop + * +@@ -240,16 +298,23 @@ static void mesh_path_move_to_queue(stru + + + static struct mesh_path *mpath_lookup(struct mesh_table *tbl, const u8 *dst, +- struct ieee80211_sub_if_data *sdata) ++ struct ieee80211_sub_if_data *sdata, ++ bool is_mpath) + { + struct mesh_path *mpath; ++ bool update; ++ struct sta_info *next_hop; + + mpath = rhashtable_lookup(&tbl->rhead, dst, mesh_rht_params); + + if (mpath && mpath_expired(mpath)) { + spin_lock_bh(&mpath->state_lock); ++ next_hop = rcu_dereference(mpath->next_hop); ++ update = !!(mpath->flags & MESH_PATH_ACTIVE); + mpath->flags &= ~MESH_PATH_ACTIVE; + spin_unlock_bh(&mpath->state_lock); ++ if (update && is_mpath) ++ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL); + } + return mpath; + } +@@ -266,13 +331,13 @@ static struct mesh_path *mpath_lookup(st + struct mesh_path * + mesh_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) + { +- return mpath_lookup(&sdata->u.mesh.mesh_paths, dst, sdata); ++ return mpath_lookup(&sdata->u.mesh.mesh_paths, dst, sdata, true); + } + + struct mesh_path * + mpp_path_lookup(struct ieee80211_sub_if_data *sdata, const u8 *dst) + { +- return mpath_lookup(&sdata->u.mesh.mpp_paths, dst, sdata); ++ return mpath_lookup(&sdata->u.mesh.mpp_paths, dst, sdata, false); + } + + static struct mesh_path * +@@ -334,6 +399,7 @@ mpp_path_lookup_by_idx(struct ieee80211_ + int mesh_path_add_gate(struct mesh_path *mpath) + { + struct mesh_table *tbl; ++ struct sta_info *next_hop; + int err; + + rcu_read_lock(); +@@ -346,6 +412,7 @@ int mesh_path_add_gate(struct mesh_path + goto err_rcu; + } + mpath->is_gate = true; ++ next_hop = rcu_dereference(mpath->next_hop); + mpath->sdata->u.mesh.num_gates++; + + spin_lock(&tbl->gates_lock); +@@ -354,6 +421,8 @@ int mesh_path_add_gate(struct mesh_path + + spin_unlock_bh(&mpath->state_lock); + ++ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL); ++ + mpath_dbg(mpath->sdata, + "Mesh path: Recorded new gate: %pM. %d known gates\n", + mpath->dst, mpath->sdata->u.mesh.num_gates); +@@ -370,16 +439,21 @@ err_rcu: + */ + static void mesh_gate_del(struct mesh_table *tbl, struct mesh_path *mpath) + { ++ struct sta_info *next_hop; ++ + lockdep_assert_held(&mpath->state_lock); + if (!mpath->is_gate) + return; + ++ next_hop = rcu_dereference(mpath->next_hop); + mpath->is_gate = false; + spin_lock_bh(&tbl->gates_lock); + hlist_del_rcu(&mpath->gate_list); + mpath->sdata->u.mesh.num_gates--; + spin_unlock_bh(&tbl->gates_lock); + ++ mesh_nss_offld_path_update(mpath, true, next_hop ? next_hop->addr : NULL); ++ + mpath_dbg(mpath->sdata, + "Mesh path: Deleted gate: %pM. %d known gates\n", + mpath->dst, mpath->sdata->u.mesh.num_gates); +@@ -667,17 +741,8 @@ void mesh_fast_tx_flush_addr(struct ieee + spin_unlock_bh(&cache->walk_lock); + } + +-/** +- * mesh_path_add - allocate and add a new path to the mesh path table +- * @dst: destination address of the path (ETH_ALEN length) +- * @sdata: local subif +- * +- * Returns: 0 on success +- * +- * State: the initial state of the new path is set to 0 +- */ +-struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, +- const u8 *dst) ++struct mesh_path *__mesh_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst) + { + struct mesh_table *tbl; + struct mesh_path *mpath, *new_mpath; +@@ -718,8 +783,36 @@ struct mesh_path *mesh_path_add(struct i + return new_mpath; + } + +-int mpp_path_add(struct ieee80211_sub_if_data *sdata, +- const u8 *dst, const u8 *mpp) ++/** ++ * mesh_path_add - allocate and add a new path to the mesh path table ++ * @dst: destination address of the path (ETH_ALEN length) ++ * @sdata: local subif ++ * ++ * Returns: 0 on success ++ * ++ * State: the initial state of the new path is set to 0 ++ */ ++struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst) ++{ ++ struct mesh_path *new_path; ++ struct ieee80211_mesh_path_offld path = {0}; ++ ++ new_path = __mesh_path_add(sdata, dst); ++ if (IS_ERR(new_path)) ++ return new_path; ++ ++ ether_addr_copy(path.mesh_da, dst); ++ ++ drv_config_mesh_offload_path(sdata->local, sdata, ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPATH, ++ &path); ++ ++ return new_path; ++} ++ ++int __mpp_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst, const u8 *mpp) + { + struct mesh_table *tbl; + struct mesh_path *new_mpath; +@@ -757,6 +850,25 @@ int mpp_path_add(struct ieee80211_sub_if + return ret; + } + ++int mpp_path_add(struct ieee80211_sub_if_data *sdata, ++ const u8 *dst, const u8 *mpp) ++{ ++ struct ieee80211_mesh_path_offld path = {0}; ++ int ret; ++ ++ ret = __mpp_path_add(sdata, dst, mpp); ++ if (ret) ++ return ret; ++ ++ ether_addr_copy(path.mesh_da, mpp); ++ ether_addr_copy(path.da, dst); ++ ++ drv_config_mesh_offload_path(sdata->local, sdata, ++ IEEE80211_MESH_PATH_OFFLD_CMD_ADD_MPP, ++ &path); ++ ++ return 0; ++} + + /** + * mesh_plink_broken - deactivates paths and sends perr when a link breaks +@@ -807,8 +919,29 @@ static void mesh_path_free_rcu(struct me + kfree_rcu(mpath, rcu); + } + +-static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath) ++static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath, ++ bool is_mpath_tbl) + { ++ struct ieee80211_mesh_path_offld path = {0}; ++ struct sta_info *next_hop; ++ struct ieee80211_sub_if_data *sdata = mpath->sdata; ++ ++ ++ path.metric = mpath->metric; ++ path.exp_time = mpath->exp_time; ++ path.hop_count = mpath->hop_count; ++ path.flags = mpath->flags; ++ if (is_mpath_tbl) { ++ ether_addr_copy(path.mesh_da, mpath->dst); ++ } else { ++ ether_addr_copy(path.mesh_da, mpath->mpp); ++ ether_addr_copy(path.da, mpath->dst); ++ } ++ ++ next_hop = rcu_dereference(mpath->next_hop); ++ if (next_hop) ++ ether_addr_copy(path.next_hop, next_hop->addr); ++ + hlist_del_rcu(&mpath->walk_list); + rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params); + if (tbl == &mpath->sdata->u.mesh.mpp_paths) +@@ -816,6 +949,11 @@ static void __mesh_path_del(struct mesh_ + else + mesh_fast_tx_flush_mpath(mpath); + mesh_path_free_rcu(tbl, mpath); ++ ++ drv_config_mesh_offload_path(sdata->local, sdata, ++ is_mpath_tbl ? IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPATH : ++ IEEE80211_MESH_PATH_OFFLD_CMD_DELETE_MPP, ++ &path); + } + + /** +@@ -839,7 +977,7 @@ void mesh_path_flush_by_nexthop(struct s + spin_lock_bh(&tbl->walk_lock); + hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) { + if (rcu_access_pointer(mpath->next_hop) == sta) +- __mesh_path_del(tbl, mpath); ++ __mesh_path_del(tbl, mpath, true); + } + spin_unlock_bh(&tbl->walk_lock); + } +@@ -854,19 +992,19 @@ static void mpp_flush_by_proxy(struct ie + spin_lock_bh(&tbl->walk_lock); + hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) { + if (ether_addr_equal(mpath->mpp, proxy)) +- __mesh_path_del(tbl, mpath); ++ __mesh_path_del(tbl, mpath, false); + } + spin_unlock_bh(&tbl->walk_lock); + } + +-static void table_flush_by_iface(struct mesh_table *tbl) ++static void table_flush_by_iface(struct mesh_table *tbl, bool is_mpath_tbl) + { + struct mesh_path *mpath; + struct hlist_node *n; + + spin_lock_bh(&tbl->walk_lock); + hlist_for_each_entry_safe(mpath, n, &tbl->walk_head, walk_list) { +- __mesh_path_del(tbl, mpath); ++ __mesh_path_del(tbl, mpath, is_mpath_tbl); + } + spin_unlock_bh(&tbl->walk_lock); + } +@@ -881,8 +1019,8 @@ static void table_flush_by_iface(struct + */ + void mesh_path_flush_by_iface(struct ieee80211_sub_if_data *sdata) + { +- table_flush_by_iface(&sdata->u.mesh.mesh_paths); +- table_flush_by_iface(&sdata->u.mesh.mpp_paths); ++ table_flush_by_iface(&sdata->u.mesh.mesh_paths, true); ++ table_flush_by_iface(&sdata->u.mesh.mpp_paths, false); + } + + /** +@@ -896,7 +1034,7 @@ void mesh_path_flush_by_iface(struct iee + */ + static int table_path_del(struct mesh_table *tbl, + struct ieee80211_sub_if_data *sdata, +- const u8 *addr) ++ const u8 *addr, bool is_mpath_tbl) + { + struct mesh_path *mpath; + +@@ -907,7 +1045,7 @@ static int table_path_del(struct mesh_ta + return -ENXIO; + } + +- __mesh_path_del(tbl, mpath); ++ __mesh_path_del(tbl, mpath, is_mpath_tbl); + spin_unlock_bh(&tbl->walk_lock); + return 0; + } +@@ -928,7 +1066,7 @@ int mesh_path_del(struct ieee80211_sub_i + /* flush relevant mpp entries first */ + mpp_flush_by_proxy(sdata, addr); + +- err = table_path_del(&sdata->u.mesh.mesh_paths, sdata, addr); ++ err = table_path_del(&sdata->u.mesh.mesh_paths, sdata, addr, true); + sdata->u.mesh.mesh_paths_generation++; + return err; + } +@@ -1031,7 +1169,10 @@ void mesh_path_flush_pending(struct mesh + */ + void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) + { ++ struct sta_info *old_next_hop; ++ + spin_lock_bh(&mpath->state_lock); ++ old_next_hop = rcu_dereference(mpath->next_hop); + mesh_path_assign_nexthop(mpath, next_hop); + mpath->sn = 0xffff; + mpath->metric = 0; +@@ -1045,6 +1186,8 @@ void mesh_path_fix_nexthop(struct mesh_p + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&next_hop->mesh->fail_avg, 1); + mesh_path_tx_pending(mpath); ++ ++ mesh_nss_offld_path_update(mpath, true, old_next_hop ? old_next_hop->addr : NULL); + } + + void mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata) +@@ -1056,7 +1199,7 @@ void mesh_pathtbl_init(struct ieee80211_ + + static + void mesh_path_tbl_expire(struct ieee80211_sub_if_data *sdata, +- struct mesh_table *tbl) ++ struct mesh_table *tbl, bool is_mpath_tbl) + { + struct mesh_path *mpath; + struct hlist_node *n; +@@ -1066,15 +1209,15 @@ void mesh_path_tbl_expire(struct ieee802 + if ((!(mpath->flags & MESH_PATH_RESOLVING)) && + (!(mpath->flags & MESH_PATH_FIXED)) && + time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) +- __mesh_path_del(tbl, mpath); ++ __mesh_path_del(tbl, mpath, is_mpath_tbl); + } + spin_unlock_bh(&tbl->walk_lock); + } + + void mesh_path_expire(struct ieee80211_sub_if_data *sdata) + { +- mesh_path_tbl_expire(sdata, &sdata->u.mesh.mesh_paths); +- mesh_path_tbl_expire(sdata, &sdata->u.mesh.mpp_paths); ++ mesh_path_tbl_expire(sdata, &sdata->u.mesh.mesh_paths, true); ++ mesh_path_tbl_expire(sdata, &sdata->u.mesh.mpp_paths, false); + } + + void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata) +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -2610,7 +2610,7 @@ static struct sk_buff *ieee80211_build_h + bool multicast; + u16 info_id = 0; + struct ieee80211_chanctx_conf *chanctx_conf = NULL; +- enum nl80211_band band; ++ enum nl80211_band band = 0; + int ret; + u8 link_id = u32_get_bits(ctrl_flags, IEEE80211_TX_CTRL_MLO_LINK); + +@@ -2622,6 +2622,9 @@ static struct sk_buff *ieee80211_build_h + info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + #endif + ++ info = IEEE80211_SKB_CB(skb); ++ memset(info, 0, sizeof(*info)); ++ + /* convert Ethernet header to proper 802.11 header (based on + * operation mode) */ + ethertype = (skb->data[12] << 8) | skb->data[13]; +@@ -2692,6 +2695,13 @@ static struct sk_buff *ieee80211_build_h + break; + #ifdef CPTCFG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: ++ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && ++ (sdata->vif.driver_flags & IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE) && ++ !(is_multicast_ether_addr(skb->data))) { ++ info->flags = IEEE80211_TX_CTL_HW_80211_ENCAP; ++ goto nss_mesh; ++ } ++ + if (!is_multicast_ether_addr(skb->data)) { + struct sta_info *next_hop; + bool mpp_lookup = true; +@@ -2955,10 +2965,10 @@ static struct sk_buff *ieee80211_build_h + + skb_reset_mac_header(skb); + +- info = IEEE80211_SKB_CB(skb); +- memset(info, 0, sizeof(*info)); +- +- info->flags = info_flags; ++#ifdef CPTCFG_MAC80211_MESH ++nss_mesh: ++#endif ++ info->flags |= info_flags; + info->ack_frame_id = info_id; + info->band = band; + +@@ -4275,6 +4285,7 @@ void __ieee80211_subif_start_xmit(struct + struct sk_buff *next; + int len = skb->len; + struct ieee80211_key *key = NULL; ++ struct ieee80211_tx_info *info; + struct ieee80211_sub_if_data *ap_sdata; + + if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { +@@ -4351,9 +4362,15 @@ void __ieee80211_subif_start_xmit(struct + goto out; + } + +- dev_sw_netstats_tx_add(dev, 1, skb->len); +- +- ieee80211_xmit(sdata, sta, skb); ++ info = IEEE80211_SKB_CB(skb); ++ if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { ++ if (sta) ++ key = rcu_dereference(sta->ptk[sta->ptk_idx]); ++ ieee80211_8023_xmit(sdata, dev, sta, key, skb); ++ } else { ++ dev_sw_netstats_tx_add(dev, 1, skb->len); ++ ieee80211_xmit(sdata, sta, skb); ++ } + } + goto out; + out_free: diff --git a/package/kernel/mac80211/patches/nss/subsys/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch b/package/kernel/mac80211/patches/nss/subsys/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch new file mode 100644 index 00000000000000..0704e8fcf7da86 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/335-0003-ath11k-skip-HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE-con.patch @@ -0,0 +1,45 @@ +From c7bd857a315fb299e4c984be2f3720428477ae6e Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 11 Nov 2021 11:14:08 +0530 +Subject: [PATCH] ath11k: skip HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE config + +Don't set HAL_TCL_DATA_CMD_INFO2_TID_OVERWRITE flag to TCL, +HW only take care of tid classification if this flag is not set. + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Gautham Kumar Senthilkumaran +--- + include/net/mac80211.h | 3 +++ + net/mac80211/debugfs.c | 1 + + net/mac80211/wme.c | 3 +++ + 3 files changed, 7 insertions(+) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -2733,6 +2733,8 @@ struct ieee80211_txq { + * + * @IEEE80211_HW_SUPPORTS_NSS_OFFLOAD: Hardware/driver supports NSS offload + * ++ * @IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD: Hardware suports tid calssification offload. ++ * + * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays + */ + enum ieee80211_hw_flags { +@@ -2792,6 +2794,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_MLO_MCAST_MULTI_LINK_TX, + IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, + IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD, ++ IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -507,6 +507,7 @@ static const char *hw_flag_names[] = { + FLAG(MLO_MCAST_MULTI_LINK_TX), + FLAG(SUPPORTS_NSS_OFFLOAD), + FLAG(SUPPORTS_MESH_NSS_OFFLOAD), ++ FLAG(SUPPORTS_TID_CLASS_OFFLOAD), + #undef FLAG + }; + diff --git a/package/kernel/mac80211/patches/nss/subsys/335-0005-mac80211-simple-tx-for-AP-mode.patch b/package/kernel/mac80211/patches/nss/subsys/335-0005-mac80211-simple-tx-for-AP-mode.patch new file mode 100644 index 00000000000000..314bcbe549af97 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/335-0005-mac80211-simple-tx-for-AP-mode.patch @@ -0,0 +1,96 @@ +From 190652ce1b56a41ed3a99d9f9c9160deba34810b Mon Sep 17 00:00:00 2001 +From: Venkateswara Naralasetty +Date: Thu, 18 Nov 2021 12:28:31 +0530 +Subject: [PATCH] mac80211: simple tx for AP mode + +Introduced new API ieee80211_8023_xmit_ap to make tx simple and +to avoid unnecessary checks for AP mode. + +Signed-off-by: Venkateswara Naralasetty +Signed-off-by: Aloka Dixit +--- + net/mac80211/tx.c | 40 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 40 insertions(+) + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4762,6 +4762,67 @@ out_free: + kfree_skb(skb); + } + ++void ieee80211_8023_xmit_ap(struct ieee80211_sub_if_data *sdata, ++ struct net_device *dev, struct sta_info *sta, ++ struct ieee80211_key *key, struct sk_buff *skb) ++{ ++ struct ieee80211_tx_info *info; ++ struct ieee80211_local *local = sdata->local; ++ struct ieee80211_sta *pubsta = NULL; ++ struct ieee80211_tx_control control = {}; ++ unsigned long flags; ++ int q; ++ u16 q_map; ++ ++ /* ++ * If the skb is shared we need to obtain our own copy. ++ */ ++ skb = skb_share_check(skb, GFP_ATOMIC); ++ ++ if (unlikely(!skb)) ++ return; ++ ++ info = IEEE80211_SKB_CB(skb); ++ memset(info, 0, sizeof(*info)); ++ ++ if (unlikely(skb->sk && ++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) ++ info->ack_frame_id = ieee80211_store_ack_skb(local, skb, ++ &info->flags, NULL); ++ ++ info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP; ++ info->control.vif = &sdata->vif; ++ ++ if (key) ++ info->control.hw_key = &key->conf; ++ ++ q_map = skb_get_queue_mapping(skb); ++ q = sdata->vif.hw_queue[q_map]; ++ ++ if (sta) { ++ sta->deflink.tx_stats.bytes[q_map] += skb->len; ++ sta->deflink.tx_stats.packets[q_map]++; ++ } ++ ++ spin_lock_irqsave(&local->queue_stop_reason_lock, flags); ++ ++ if (local->queue_stop_reasons[q] || !skb_queue_empty(&local->pending[q])) { ++ skb_queue_tail(&local->pending[q], skb); ++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); ++ return; ++ } ++ ++ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); ++ ++ if (sta && sta->uploaded) ++ pubsta = &sta->sta; ++ ++ control.sta = pubsta; ++ ++ drv_tx(local, &control, skb); ++ ++} ++ + netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) + { +@@ -4801,6 +4862,11 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))) + goto skip_offload; + ++ if (sdata->vif.type == NL80211_IFTYPE_AP) { ++ ieee80211_8023_xmit_ap(sdata, dev, sta, key, skb); ++ goto out; ++ } ++ + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); + tx_offload: + ieee80211_8023_xmit(sdata, dev, sta, key, skb); diff --git a/package/kernel/mac80211/patches/nss/subsys/336-mac80211-Mesh-Fast-rx-support.patch b/package/kernel/mac80211/patches/nss/subsys/336-mac80211-Mesh-Fast-rx-support.patch new file mode 100644 index 00000000000000..0b89a11957bec4 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/336-mac80211-Mesh-Fast-rx-support.patch @@ -0,0 +1,208 @@ +From 0f024902a8a54c70204f5b2f824c5dc74888c536 Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Wed, 29 Sep 2021 09:30:21 +0530 +Subject: [PATCH] mac80211: Add support for mesh fast Rx path + +Add support to process rx frames for the mesh destination +when driver supports fast Rx by offloading PN, Duplicate, +reordering to the HW. + +Fast Rx from a peer is enabled once the PLINK is established. +Fast Rx is not supported for the forwarding path currently. + +Signed-off-by: Sriram R +--- + net/mac80211/cfg.c | 5 + + net/mac80211/ieee80211_i.h | 1 + + net/mac80211/mesh_plink.c | 5 + + net/mac80211/rx.c | 262 ++++++++++++++++++++++++++++++++++++++++++++- + 4 files changed, 269 insertions(+), 4 deletions(-) + +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -1750,6 +1750,8 @@ static void sta_apply_mesh_params(struct + /* init at low value */ + ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, 10); + ++ ieee80211_check_fast_rx(sta); ++ + break; + case NL80211_PLINK_LISTEN: + case NL80211_PLINK_BLOCKED: +@@ -1764,6 +1766,7 @@ static void sta_apply_mesh_params(struct + ieee80211_mps_sta_status_update(sta); + changed |= ieee80211_mps_set_sta_local_pm(sta, + NL80211_MESH_POWER_UNKNOWN); ++ ieee80211_check_fast_rx(sta); + break; + default: + /* nothing */ +--- a/net/mac80211/mesh_plink.c ++++ b/net/mac80211/mesh_plink.c +@@ -381,6 +381,8 @@ static u64 __mesh_plink_deactivate(struc + changed |= ieee80211_mps_set_sta_local_pm(sta, + NL80211_MESH_POWER_UNKNOWN); + ++ ieee80211_check_fast_rx(sta); ++ + return changed; + } + +@@ -846,6 +848,7 @@ static u64 mesh_plink_establish(struct i + mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); + ieee80211_mps_sta_status_update(sta); + changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); ++ ieee80211_check_fast_rx(sta); + return changed; + } + +@@ -864,7 +867,7 @@ static u64 mesh_plink_fsm(struct ieee802 + struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; + enum ieee80211_self_protected_actioncode action = 0; + u64 changed = 0; +- bool flush = false; ++ bool flush = false, check_fast_rx = false; + + mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, + mplstates[sta->mesh->plink_state], mplevents[event]); +@@ -924,6 +927,7 @@ static u64 mesh_plink_fsm(struct ieee802 + break; + case CNF_ACPT: + changed |= mesh_plink_establish(sdata, sta); ++ check_fast_rx = true; + break; + default: + break; +@@ -939,6 +943,7 @@ static u64 mesh_plink_fsm(struct ieee802 + break; + case OPN_ACPT: + changed |= mesh_plink_establish(sdata, sta); ++ check_fast_rx = true; + action = WLAN_SP_MESH_PEERING_CONFIRM; + break; + default: +@@ -985,6 +990,10 @@ static u64 mesh_plink_fsm(struct ieee802 + break; + } + spin_unlock_bh(&sta->mesh->plink_lock); ++ ++ if (check_fast_rx) ++ ieee80211_check_fast_rx(sta); ++ + if (flush) + mesh_path_flush_by_nexthop(sta); + if (action) { +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -4649,10 +4649,15 @@ void ieee80211_check_fast_rx(struct sta_ + + break; + case NL80211_IFTYPE_MESH_POINT: ++ /* Not required for NSS mode */ ++ if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) ++ goto clear; ++ /* Note: da and sa offs are not static, determine in fast rx path */ ++ + fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); +- fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3); +- fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr4); ++ ++ fastrx.internal_forward = 0; + break; + default: + goto clear; +@@ -4693,7 +4698,7 @@ void ieee80211_check_fast_rx(struct sta_ + __release(check_fast_rx); + + if (assign) +- new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL); ++ new = kmemdup(&fastrx, sizeof(fastrx), GFP_ATOMIC); + + offload_flags = get_bss_sdata(sdata)->vif.offload_flags; + offload = offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED; +@@ -4875,6 +4880,10 @@ static bool ieee80211_invoke_fast_rx(str + u8 sa[ETH_ALEN]; + } addrs __aligned(2); + struct ieee80211_sta_rx_stats *stats; ++ struct ieee80211s_hdr *mesh_hdr; ++ struct mesh_path *mppath; ++ u8 da_offs = fast_rx->da_offs, sa_offs = fast_rx->sa_offs; ++ struct ieee80211_sub_if_data *sdata = rx->sdata; + + /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write + * to a common data structure; drivers can implement that per queue +@@ -4924,6 +4933,37 @@ static bool ieee80211_invoke_fast_rx(str + snap_offs += IEEE80211_CCMP_HDR_LEN; + } + ++ /* Find corresponding offsets for mesh hdr */ ++ if (ieee80211_vif_is_mesh(&sdata->vif)) { ++ if (status->rx_flags & IEEE80211_RX_AMSDU) ++ return false; ++ ++ /* All mesh data frames needs to be QoS Data */ ++ if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) ++ return false; ++ ++ /* TODO forwarding not handled yet in fast rx */ ++ if (!ether_addr_equal(fast_rx->vif_addr, hdr->addr3)) ++ return false; ++ ++ /* Check if Min Mesh hdr is present */ ++ if (!pskb_may_pull(skb, hdrlen + 6)) ++ goto drop; ++ ++ /* Goto mesh hdr, located at snap offs compared to AP/STA */ ++ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + snap_offs); ++ ++ /* Only Ext Mesh hdr supported in this path now */ ++ if ((mesh_hdr->flags & MESH_FLAGS_AE) != MESH_FLAGS_AE_A5_A6) ++ return false; ++ ++ /* Point to eaddr1 and eaddr2 */ ++ da_offs = snap_offs + ETH_ALEN; ++ sa_offs = da_offs + ETH_ALEN; ++ ++ snap_offs += sizeof(struct ieee80211s_hdr); ++ } ++ + if (!ieee80211_vif_is_mesh(&rx->sdata->vif) && + !(status->rx_flags & IEEE80211_RX_AMSDU)) { + if (!pskb_may_pull(skb, snap_offs + sizeof(*payload))) +@@ -4961,9 +5001,33 @@ static bool ieee80211_invoke_fast_rx(str + return true; + } + ++ /* Update MPP table for the received packet */ ++ if (ieee80211_vif_is_mesh(&sdata->vif)) { ++ char *proxied_addr, *mpp_addr; ++ ++ mpp_addr = hdr->addr4; ++ proxied_addr = mesh_hdr->eaddr2; ++ ++ /* Update mpp for the SA */ ++ rcu_read_lock(); ++ mppath = mpp_path_lookup(sdata, proxied_addr); ++ if (!mppath) { ++ mpp_path_add(sdata, proxied_addr, mpp_addr); ++ } else { ++ spin_lock_bh(&mppath->state_lock); ++ ++ if (!ether_addr_equal(mppath->mpp, mpp_addr)) ++ ether_addr_copy(mppath->mpp, mpp_addr); ++ ++ mppath->exp_time = jiffies; ++ spin_unlock_bh(&mppath->state_lock); ++ } ++ rcu_read_unlock(); ++ } ++ + /* do the header conversion - first grab the addresses */ +- ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs); +- ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs); ++ ether_addr_copy(addrs.da, skb->data + da_offs); ++ ether_addr_copy(addrs.sa, skb->data + sa_offs); + if (ieee80211_vif_is_mesh(&rx->sdata->vif)) { + skb_pull(skb, snap_offs - 2); + put_unaligned_be16(skb->len - 2, skb->data); diff --git a/package/kernel/mac80211/patches/nss/subsys/342-mac80211-fix-unconditional-sta-usage.patch b/package/kernel/mac80211/patches/nss/subsys/342-mac80211-fix-unconditional-sta-usage.patch new file mode 100644 index 00000000000000..c8d719006d170f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/342-mac80211-fix-unconditional-sta-usage.patch @@ -0,0 +1,53 @@ +From 479096a023928cc75aa38953b7170a8984acd0da Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam +Date: Tue, 11 Jan 2022 14:04:09 +0530 +Subject: [PATCH] mac80211: Fix kernel panic due to unsafe sta usage + +Observing below crash in dynamic vlan scneario when +abruptly killing hostapd while ping or any traffic to stations +are going on. + +[ 753.307213] Unable to handle kernel NULL pointer dereference at virtual address 0000058c +[ 753.309137] pgd = 7514769a +[ 753.317392] [0000058c] *pgd=00000000 +[ 753.319892] Internal error: Oops: 5 [#1] PREEMPT SMP ARM +[ 753.604280] PC is at __ieee80211_subif_start_xmit+0xc58/0xe48 [mac80211] +[ 753.608954] LR is at __ieee80211_subif_start_xmit+0xc3c/0xe48 [mac80211] +[ 753.615729] pc : [] lr : [] psr: 40000013 +[ 753.622411] sp : 843b5940 ip : 98e7d348 fp : 99463e42 +[ 753.628398] r10: 98e7d318 r9 : 92d0e000 r8 : 00000000 +[ 753.633606] r7 : 963c8d20 r6 : 92d0e580 r5 : 00000000 r4 : 98e7d300 +[ 753.638819] r3 : 00000163 r2 : fffffff0 r1 : 00000000 r0 : 98e7d318 +[ 753.645416] Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user +[ 753.651928] Control: 10c0383d Table: 5db8806a DAC: 00000055 +[ 753.659135] Process ping (pid: 4436, stack limit = 0xf466aee4) + +Its due to accessing the sta pointer +unconditionally. Fix that by checking sta pointer is +available or not before using. + +Signed-off-by: Tamizh Chelvam +--- + net/mac80211/tx.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -4696,7 +4696,7 @@ static void ieee80211_8023_xmit(struct i + + ieee80211_aggr_check(sdata, sta, skb); + +- if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && sta) { + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); + if (tid_tx) { +@@ -4747,7 +4747,7 @@ static void ieee80211_8023_xmit(struct i + &info->flags, NULL); + + dev_sw_netstats_tx_add(dev, skbs, len); +- if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { ++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && sta) { + sta->deflink.tx_stats.packets[queue] += skbs; + sta->deflink.tx_stats.bytes[queue] += len; + } diff --git a/package/kernel/mac80211/patches/nss/subsys/345-mac80211-fix-mixed-declaration.patch b/package/kernel/mac80211/patches/nss/subsys/345-mac80211-fix-mixed-declaration.patch new file mode 100644 index 00000000000000..808f69e644ed61 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/345-mac80211-fix-mixed-declaration.patch @@ -0,0 +1,52 @@ +From b3215eee07d071137e6977d60eee3cf685241fbb Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Thu, 3 Feb 2022 13:55:53 +0530 +Subject: [PATCH] mac80211 : fix mixed declaration + +Fix mixed declaration in the api ieee80211_parse_ch_switch_ie + +Signed-off-by: Hari Chandrakanthan +--- + net/mac80211/spectmgmt.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +--- a/net/mac80211/spectmgmt.c ++++ b/net/mac80211/spectmgmt.c +@@ -33,7 +33,10 @@ int ieee80211_parse_ch_switch_ie(struct + struct cfg80211_chan_def new_vht_chandef = {}; + const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; + const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; ++ struct ieee80211_vht_operation vht_oper; ++ struct ieee80211_ht_operation ht_oper; + int secondary_channel_offset = -1; ++ u8 new_seg1; + + memset(csa_ie, 0, sizeof(*csa_ie)); + +@@ -133,20 +136,13 @@ int ieee80211_parse_ch_switch_ie(struct + } + + if (wide_bw_chansw_ie) { +- u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; +- struct ieee80211_vht_operation vht_oper = { +- .chan_width = +- wide_bw_chansw_ie->new_channel_width, +- .center_freq_seg0_idx = +- wide_bw_chansw_ie->new_center_freq_seg0, +- .center_freq_seg1_idx = new_seg1, ++ new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; ++ vht_oper.chan_width = wide_bw_chansw_ie->new_channel_width; ++ vht_oper.center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0; ++ vht_oper.center_freq_seg1_idx = new_seg1; + /* .basic_mcs_set doesn't matter */ +- }; +- struct ieee80211_ht_operation ht_oper = { +- .operation_mode = +- cpu_to_le16(new_seg1 << +- IEEE80211_HT_OP_MODE_CCFS2_SHIFT), +- }; ++ ht_oper.operation_mode = cpu_to_le16(new_seg1 << ++ IEEE80211_HT_OP_MODE_CCFS2_SHIFT); + + /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, + * to the previously parsed chandef diff --git a/package/kernel/mac80211/patches/nss/subsys/346-mac80211-fix-bw-change-to-40Mhz-during-channel-switc.patch b/package/kernel/mac80211/patches/nss/subsys/346-mac80211-fix-bw-change-to-40Mhz-during-channel-switc.patch new file mode 100644 index 00000000000000..df2b3a9257b115 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/346-mac80211-fix-bw-change-to-40Mhz-during-channel-switc.patch @@ -0,0 +1,34 @@ +From 9c7571646a01eedb85350dfce12b499a0267ab2b Mon Sep 17 00:00:00 2001 +From: Hari Chandrakanthan +Date: Thu, 3 Feb 2022 14:01:57 +0530 +Subject: [PATCH] mac80211 : fix bw change to 40Mhz during channel switch + +When AP reduces its channel bandwidth to 40Mhz, the associated +sta reduces the channel bandwidth to 20Mhz. + +From spec 802.11 ac, section 8.4.2.165 : +The Wide Bandwidth Channel Switch subelement is present under the following conditions: +1.Channel switching to a BSS operating channel width of 40 MHz or wider +2.Extended channel switching to a BSS operating channel width of 80 MHz or wider + +So when wide bandwidth channel switch subelement is present, +the default bandwidth is chosen as 40Mhz. + +Signed-off-by: Hari Chandrakanthan +--- + net/mac80211/spectmgmt.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/net/mac80211/spectmgmt.c ++++ b/net/mac80211/spectmgmt.c +@@ -136,6 +136,10 @@ int ieee80211_parse_ch_switch_ie(struct + } + + if (wide_bw_chansw_ie) { ++ csa_ie->chandef.width = NL80211_CHAN_WIDTH_40; ++ csa_ie->chandef.center_freq1 = ++ ieee80211_channel_to_frequency(wide_bw_chansw_ie->new_center_freq_seg0, ++ new_chan->band); + new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; + vht_oper.chan_width = wide_bw_chansw_ie->new_channel_width; + vht_oper.center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0; diff --git a/package/kernel/mac80211/patches/nss/subsys/353-mac80211-fix-dynamic-vlan-warning-with-monitor-interface-restart.patch b/package/kernel/mac80211/patches/nss/subsys/353-mac80211-fix-dynamic-vlan-warning-with-monitor-interface-restart.patch new file mode 100644 index 00000000000000..ba80b62d0ac732 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/353-mac80211-fix-dynamic-vlan-warning-with-monitor-interface-restart.patch @@ -0,0 +1,33 @@ +From 0628e831520aa2e57aed02aee4a1772b40ce4f9d Mon Sep 17 00:00:00 2001 +From: Nagarajan Maran +Date: Thu, 30 Jun 2022 17:20:29 +0530 +Subject: [PATCH] mac80211: fix dynamic vlan warning with monitor interface restart + +When monitor interface restarts, in nss offload disabled +case, the encap and decap offload flags are removed +from all the interfaces in that phy#. + +However when dynamic VLAN and monitor interfaces are +created in the same phy#, these flags are not updated +correctly, due to which warning calltrace is observed. + +Add condition check to update the correct flags in +dynamic VLAN case. + +Signed-off-by: Nagarajan Maran +--- + net/mac80211/iface.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -998,7 +998,8 @@ static bool ieee80211_set_sdata_offload_ + flags |= IEEE80211_OFFLOAD_DECAP_ENABLED; + + if (local->monitors && +- !ieee80211_hw_check(&local->hw, SUPPORTS_CONC_MON_RX_DECAP)) ++ (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) || ++ !ieee80211_hw_check(&local->hw, SUPPORTS_CONC_MON_RX_DECAP))) + flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; + } else { + flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED; diff --git a/package/kernel/mac80211/patches/nss/subsys/640-006-01-mac80211-Remove-unused-RX_FLAGS-from-mac80211_rx_fla.patch b/package/kernel/mac80211/patches/nss/subsys/640-006-01-mac80211-Remove-unused-RX_FLAGS-from-mac80211_rx_fla.patch new file mode 100644 index 00000000000000..55380f59d3826c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/640-006-01-mac80211-Remove-unused-RX_FLAGS-from-mac80211_rx_fla.patch @@ -0,0 +1,85 @@ +From 11d0cce62afc157468e1d97ea80a2510091ea2c2 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Fri, 1 Jul 2022 11:57:00 +0530 +Subject: [PATCH] mac80211: Remove unused RX_FLAGS from mac80211_rx_flags + +Remove unused RX_FLAG_AMPDU_DELIM_CRC_KNOWN flag from +mac80211_rx_flags to provide space for new EHT flags. + +Signed-off-by: P Praneesh +--- + include/net/mac80211.h | 33 +++++++++++++++------------------ + net/mac80211/rx.c | 7 +------ + 2 files changed, 16 insertions(+), 24 deletions(-) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -1407,8 +1407,6 @@ ieee80211_tx_info_clear_status(struct ie + * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU + * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected + * on this subframe +- * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC +- * is stored in the @ampdu_delimiter_crc field) + * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was + * done by the hardware + * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without +@@ -1480,22 +1478,21 @@ enum mac80211_rx_flags { + RX_FLAG_AMPDU_LAST_KNOWN = BIT(12), + RX_FLAG_AMPDU_IS_LAST = BIT(13), + RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14), +- RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15), +- RX_FLAG_MACTIME_END = BIT(16), +- RX_FLAG_ONLY_MONITOR = BIT(17), +- RX_FLAG_SKIP_MONITOR = BIT(18), +- RX_FLAG_AMSDU_MORE = BIT(19), +- RX_FLAG_RADIOTAP_TLV_AT_END = BIT(20), +- RX_FLAG_MIC_STRIPPED = BIT(21), +- RX_FLAG_ALLOW_SAME_PN = BIT(22), +- RX_FLAG_ICV_STRIPPED = BIT(23), +- RX_FLAG_AMPDU_EOF_BIT = BIT(24), +- RX_FLAG_AMPDU_EOF_BIT_KNOWN = BIT(25), +- RX_FLAG_RADIOTAP_HE = BIT(26), +- RX_FLAG_RADIOTAP_HE_MU = BIT(27), +- RX_FLAG_RADIOTAP_LSIG = BIT(28), +- RX_FLAG_NO_PSDU = BIT(29), +- RX_FLAG_8023 = BIT(30), ++ RX_FLAG_MACTIME_END = BIT(15), ++ RX_FLAG_ONLY_MONITOR = BIT(16), ++ RX_FLAG_SKIP_MONITOR = BIT(17), ++ RX_FLAG_AMSDU_MORE = BIT(18), ++ RX_FLAG_RADIOTAP_TLV_AT_END = BIT(19), ++ RX_FLAG_MIC_STRIPPED = BIT(20), ++ RX_FLAG_ALLOW_SAME_PN = BIT(21), ++ RX_FLAG_ICV_STRIPPED = BIT(22), ++ RX_FLAG_AMPDU_EOF_BIT = BIT(23), ++ RX_FLAG_AMPDU_EOF_BIT_KNOWN = BIT(24), ++ RX_FLAG_RADIOTAP_HE = BIT(25), ++ RX_FLAG_RADIOTAP_HE_MU = BIT(26), ++ RX_FLAG_RADIOTAP_LSIG = BIT(27), ++ RX_FLAG_NO_PSDU = BIT(28), ++ RX_FLAG_8023 = BIT(29), + }; + + /** +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -507,18 +507,13 @@ ieee80211_add_rx_radiotap_header(struct + flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST; + if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR) + flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR; +- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +- flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN; + if (status->flag & RX_FLAG_AMPDU_EOF_BIT_KNOWN) + flags |= IEEE80211_RADIOTAP_AMPDU_EOF_KNOWN; + if (status->flag & RX_FLAG_AMPDU_EOF_BIT) + flags |= IEEE80211_RADIOTAP_AMPDU_EOF; + put_unaligned_le16(flags, pos); + pos += 2; +- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN) +- *pos++ = status->ampdu_delimiter_crc; +- else +- *pos++ = 0; ++ *pos++ = 0; + *pos++ = 0; + } + diff --git a/package/kernel/mac80211/patches/nss/subsys/657-mac80211-Avoid-encapsulation-of-EAPOL-frames-if-OFFL.patch b/package/kernel/mac80211/patches/nss/subsys/657-mac80211-Avoid-encapsulation-of-EAPOL-frames-if-OFFL.patch new file mode 100644 index 00000000000000..13977ab665862e --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/657-mac80211-Avoid-encapsulation-of-EAPOL-frames-if-OFFL.patch @@ -0,0 +1,206 @@ +From f2c96599c02eb0d47602d000fe0f40358c10c892 Mon Sep 17 00:00:00 2001 +From: Aaradhana Sahu +Date: Fri, 26 Aug 2022 17:54:37 +0530 +Subject: [PATCH] mac80211: Avoid encapsulation of EAPOL frames if OFFLOAD_ENCAP is enabled + +EAP Frames over NL80211 Control port are 802.11 Native WiFi +encapsulated by default, but for a vdev operating in 802.3, when FW doesn’t +advertise WMI_SERVICE_EAPOL_OVER_NWIFI, it cannot accept an 802.11 +Native WiFi frame. + +Allow EAP Frames over NL80211 Control port to be passed as 802.3 +if vif has IEEE80211_OFFLOAD_ENCAP_ENABLED set. + +Signed-off-by: Rameshkumar Sundaram +Signed-off-by: Aaradhana Sahu +--- + net/mac80211/ieee80211_i.h | 5 ++++ + net/mac80211/tx.c | 58 ++++++++++++++++++++++++++------------ + 2 files changed, 45 insertions(+), 18 deletions(-) + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -2063,6 +2063,11 @@ netdev_tx_t ieee80211_subif_start_xmit(s + struct net_device *dev); + netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev); ++netdev_tx_t __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, ++ struct net_device *dev, ++ u32 info_flags, ++ u32 ctrl_flags, ++ u64 *cookie); + void __ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev, + u32 info_flags, +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -39,7 +39,8 @@ + + static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, + struct net_device *dev, struct sta_info *sta, +- struct ieee80211_key *key, struct sk_buff *skb); ++ struct ieee80211_key *key, struct sk_buff *skb, ++ u32 info_flags, u32 ctrl_flags, u64 *cookie); + /* misc utils */ + + static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, +@@ -4320,7 +4321,7 @@ void __ieee80211_subif_start_xmit(struct + !is_multicast_ether_addr(skb->data)) { + if (sta) + key = rcu_dereference(sta->ptk[sta->ptk_idx]); +- ieee80211_8023_xmit(sdata, dev, sta, key, skb); ++ ieee80211_8023_xmit(sdata, dev, sta, key, skb, info_flags, ctrl_flags, cookie); + rcu_read_unlock(); + return; + } +@@ -4366,7 +4367,7 @@ void __ieee80211_subif_start_xmit(struct + if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { + if (sta) + key = rcu_dereference(sta->ptk[sta->ptk_idx]); +- ieee80211_8023_xmit(sdata, dev, sta, key, skb); ++ ieee80211_8023_xmit(sdata, dev, sta, key, skb, info_flags, ctrl_flags, cookie); + } else { + dev_sw_netstats_tx_add(dev, 1, skb->len); + ieee80211_xmit(sdata, sta, skb); +@@ -4664,7 +4665,8 @@ static bool ieee80211_tx_8023(struct iee + + static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, + struct net_device *dev, struct sta_info *sta, +- struct ieee80211_key *key, struct sk_buff *skb) ++ struct ieee80211_key *key, struct sk_buff *skb, ++ u32 info_flags, u32 ctrl_flags, u64 *cookie) + { + struct ieee80211_tx_info *info; + struct ethhdr *ehdr = (struct ethhdr *)skb->data; +@@ -4720,6 +4722,7 @@ static void ieee80211_8023_xmit(struct i + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + ++ info->flags |= info_flags; + info->hw_queue = sdata->vif.hw_queue[queue]; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +@@ -4740,11 +4743,12 @@ static void ieee80211_8023_xmit(struct i + memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); + } + +- if (unlikely(skb->sk && +- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && ++ if (unlikely(((skb->sk && ++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) || ++ ((ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS) && !multicast)) && + !ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD))) + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, +- &info->flags, NULL); ++ &info->flags, cookie); + + dev_sw_netstats_tx_add(dev, skbs, len); + if (!ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD) && sta) { +@@ -4764,7 +4768,8 @@ out_free: + + void ieee80211_8023_xmit_ap(struct ieee80211_sub_if_data *sdata, + struct net_device *dev, struct sta_info *sta, +- struct ieee80211_key *key, struct sk_buff *skb) ++ struct ieee80211_key *key, struct sk_buff *skb, ++ u32 info_flags, u32 ctrl_flags, u64 *cookie) + { + struct ieee80211_tx_info *info; + struct ieee80211_local *local = sdata->local; +@@ -4773,6 +4778,9 @@ void ieee80211_8023_xmit_ap(struct ieee8 + unsigned long flags; + int q; + u16 q_map; ++ struct ethhdr *ehdr = (struct ethhdr *)skb->data; ++ unsigned char *ra = ehdr->h_dest; ++ bool multicast = is_multicast_ether_addr(ra); + + /* + * If the skb is shared we need to obtain our own copy. +@@ -4784,11 +4792,13 @@ void ieee80211_8023_xmit_ap(struct ieee8 + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); ++ info->flags |= info_flags; + +- if (unlikely(skb->sk && +- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) ++ if (unlikely((skb->sk && ++ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) || ++ ((ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS) && !multicast))) + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, +- &info->flags, NULL); ++ &info->flags, cookie); + + info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP; + info->control.vif = &sdata->vif; +@@ -4822,14 +4832,23 @@ void ieee80211_8023_xmit_ap(struct ieee8 + drv_tx(local, &control, skb); + + } +- + netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) + { ++ return __ieee80211_subif_start_xmit_8023(skb, dev, 0, 0, NULL); ++} ++ ++netdev_tx_t __ieee80211_subif_start_xmit_8023(struct sk_buff *skb, ++ struct net_device *dev, ++ u32 info_flags, ++ u32 ctrl_flags, ++ u64 *cookie) ++{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_key *key = NULL; + struct sta_info *sta; ++ bool is_eapol; + + #ifdef CPTCFG_MAC80211_NSS_SUPPORT + ieee80211_xmit_nss_fixup(skb, dev); +@@ -4845,14 +4864,15 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + kfree_skb(skb); + goto out; + } ++ is_eapol = (sdata->control_port_protocol == ehdr->h_proto); + + if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NSS_OFFLOAD)) { + if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded)) + sta = NULL; + goto tx_offload; + } else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || +- !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || +- sdata->control_port_protocol == ehdr->h_proto)) ++ (!test_sta_flag(sta, WLAN_STA_AUTHORIZED) && !is_eapol) || ++ (is_eapol && !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)))) + goto skip_offload; + + key = rcu_dereference(sta->ptk[sta->ptk_idx]); +@@ -4863,13 +4883,13 @@ netdev_tx_t ieee80211_subif_start_xmit_8 + goto skip_offload; + + if (sdata->vif.type == NL80211_IFTYPE_AP) { +- ieee80211_8023_xmit_ap(sdata, dev, sta, key, skb); ++ ieee80211_8023_xmit_ap(sdata, dev, sta, key, skb, info_flags, ctrl_flags, cookie); + goto out; + } + + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); + tx_offload: +- ieee80211_8023_xmit(sdata, dev, sta, key, skb); ++ ieee80211_8023_xmit(sdata, dev, sta, key, skb, info_flags, ctrl_flags, cookie); + goto out; + + skip_offload: +@@ -6375,7 +6395,10 @@ start_xmit: + mutex_lock(&local->mtx); + + local_bh_disable(); +- __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); ++ if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) ++ __ieee80211_subif_start_xmit_8023(skb, skb->dev, flags, ctrl_flags, cookie); ++ else ++ __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); + local_bh_enable(); + + mutex_unlock(&local->mtx); diff --git a/package/kernel/mac80211/patches/nss/subsys/686-mac80211-fix-RCU-stall-in-mesh-fast-xmit-path.patch b/package/kernel/mac80211/patches/nss/subsys/686-mac80211-fix-RCU-stall-in-mesh-fast-xmit-path.patch new file mode 100644 index 00000000000000..62916f15eddb56 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/686-mac80211-fix-RCU-stall-in-mesh-fast-xmit-path.patch @@ -0,0 +1,114 @@ +From 331198f889cef552e4644abf1f2ebfcaa2cc41e9 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Wed, 23 Nov 2022 18:50:47 +0530 +Subject: [PATCH] mac80211: fix RCU stall in mesh fast xmit path + +In mesh fast xmit, mesh_fill_cached_hdr tries to acquire spinlock +which is already acquired by the same core for updating mesh path +table. Fix it by using atomic variable instead of using spinlock. + +[100466.097939] rcu: INFO: rcu_preempt self-detected stall on CPU +[100466.097962] rcu: 0-....: (8381 ticks this GP) idle=e86/0/0x3 softirq=2648463/2648463 fqs=4184 +[100466.102651] (t=8403 jiffies g=5521597 q=620) +[100466.111586] Task dump for CPU 0: +[100466.115839] swapper/0 R running task 0 0 0 0x0000000a +[100466.119228] Call trace: +[100466.126348] dump_backtrace+0x0/0x15c +[100466.128949] show_stack+0x14/0x1c +[100466.132508] sched_show_task+0x104/0x134 +[100466.135893] dump_cpu_task+0x40/0x274 +[100466.139974] rcu_dump_cpu_stacks+0x7c/0xd4 +[100466.143620] rcu_sched_clock_irq+0x350/0x824 +[100466.147699] update_process_times+0x2c/0x50 +[100466.152213] tick_sched_handle.isra.4+0x3c/0x44 +[100466.156553] tick_sched_timer+0x48/0x88 +[100466.161153] __hrtimer_run_queues+0xa0/0x140 +[100466.165059] hrtimer_interrupt+0xe4/0x214 +[100466.169315] arch_timer_handler_virt+0x28/0x3c +[100466.173308] handle_percpu_devid_irq+0x84/0x12c +[100466.177733] generic_handle_irq+0x18/0x2c +[100466.182593] __handle_domain_irq+0x84/0xac +[100466.186500] gic_handle_irq+0x74/0xbc +[100466.190579] el1_irq+0xf0/0x1c0 +[100466.194398] queued_spin_lock_slowpath+0x98/0x2c0 +[100466.197800] mesh_fill_cached_hdr+0x15c/0x2d0 [mac80211] +[100466.202397] __ieee80211_subif_start_xmit+0xf4/0xf3c [mac80211] +[100466.207865] ieee80211_subif_start_xmit+0x274/0x2ac [mac80211] +[100466.213933] dev_hard_start_xmit+0x1b0/0x230 +[100466.219574] sch_direct_xmit+0xbc/0x300 +[100466.224086] __dev_queue_xmit+0x5b0/0x8cc +[100466.228079] dev_queue_xmit+0x10/0x18 +[100466.231990] sfe_ipv4_recv_udp+0x1014/0x1050 [qca_nss_sfe] +[100466.235722] sfe_ipv4_recv+0x394/0x5a4 [qca_nss_sfe] +[100466.241190] sfe_recv+0xf0/0x47c [qca_nss_sfe] +[100466.246397] __netif_receive_skb_core+0x1ac/0xa3c +[100466.250736] __netif_receive_skb_list_core+0x84/0x1ec +[100466.255598] netif_receive_skb_list_internal+0x250/0x29c +[100466.260720] gro_normal_list+0x24/0x40 +[100466.266186] gro_normal_one+0x3c/0x48 +[100466.269832] napi_gro_receive+0xc0/0x104 +[100466.273655] edma_rx_napi_poll+0x820/0xdb4 [qca_nss_dp] +[100466.277734] __napi_poll+0x30/0xa4 +[100466.283113] net_rx_action+0x118/0x270 +[100466.286325] __do_softirq+0x10c/0x244 +[100466.290145] irq_exit+0x64/0xb4 +[100466.293965] __handle_domain_irq+0x88/0xac +[100466.297350] gic_handle_irq+0x74/0xbc +[100466.301256] el1_irq+0xf0/0x1c0 +[100466.305076] arch_cpu_idle+0x10/0x18 +[100466.308461] do_idle+0x104/0x248 +[100466.312019] cpu_startup_entry+0x20/0x64 +[100466.315320] rest_init+0xd0/0xdc +[100466.319311] arch_call_rest_init+0xc/0x14 +[100466.322610] start_kernel+0x480/0x4b8 + +Signed-off-by: P Praneesh +--- + net/mac80211/cfg.c | 2 +- + net/mac80211/mesh.h | 4 ++-- + net/mac80211/mesh_hwmp.c | 4 ++-- + net/mac80211/mesh_pathtbl.c | 9 ++++----- + 4 files changed, 9 insertions(+), 10 deletions(-) + +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2349,7 +2349,7 @@ static void mpath_set_pinfo(struct mesh_ + if (mpath->flags & MESH_PATH_RESOLVED) + pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED; + pinfo->hop_count = mpath->hop_count; +- pinfo->path_change_count = mpath->path_change_count; ++ pinfo->path_change_count = atomic_read(&mpath->path_change_count); + } + + static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, +--- a/net/mac80211/mesh.h ++++ b/net/mac80211/mesh.h +@@ -126,7 +126,7 @@ struct mesh_path { + unsigned long fast_tx_check; + bool is_root; + bool is_gate; +- u32 path_change_count; ++ atomic_t path_change_count; + }; + + #define MESH_FAST_TX_CACHE_MAX_SIZE 512 +--- a/net/mac80211/mesh_hwmp.c ++++ b/net/mac80211/mesh_hwmp.c +@@ -504,7 +504,7 @@ static u32 hwmp_route_info_get(struct ie + if (next_hop) + ether_addr_copy(old_next_hop_addr, next_hop->sta.addr); + if (next_hop != sta) { +- mpath->path_change_count++; ++ atomic_inc(&mpath->path_change_count); + flush_mpath = true; + } + mesh_path_assign_nexthop(mpath, sta); +@@ -565,7 +565,7 @@ static u32 hwmp_route_info_get(struct ie + if (next_hop) + ether_addr_copy(old_next_hop_addr, next_hop->sta.addr); + if (next_hop != sta) { +- mpath->path_change_count++; ++ atomic_inc(&mpath->path_change_count); + flush_mpath = true; + } + mesh_path_assign_nexthop(mpath, sta); diff --git a/package/kernel/mac80211/patches/nss/subsys/751-mac80211-Get-valid-last_rate-for-rx_bitrate-from-cpu.patch b/package/kernel/mac80211/patches/nss/subsys/751-mac80211-Get-valid-last_rate-for-rx_bitrate-from-cpu.patch new file mode 100644 index 00000000000000..ea83021014ad26 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/751-mac80211-Get-valid-last_rate-for-rx_bitrate-from-cpu.patch @@ -0,0 +1,68 @@ +From eac6bea547505fc6545014755e8e529fd804df42 Mon Sep 17 00:00:00 2001 +From: Maharaja Kennadyrajan +Date: Tue, 18 Apr 2023 14:41:05 +0530 +Subject: [PATCH 1/3] mac80211: Get valid last_rate for rx_bitrate from cpu + stats + +Get the valid last_rate from the cpu rx_stats while filling the +rx_bitrate in the station dump. This helps to avoid the missing +rx bitrate field in the iw station dump. + +Signed-off-by: Tamizh Chelvam Raja +Signed-off-by: Maharaja Kennadyrajan +--- + net/mac80211/sta_info.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -2385,7 +2385,7 @@ void ieee80211_sta_update_pending_airtim + } + + static struct ieee80211_sta_rx_stats * +-sta_get_last_rx_stats(struct sta_info *sta) ++sta_get_last_rx_stats(struct sta_info *sta, bool is_rx_bitrate) + { + struct ieee80211_sta_rx_stats *stats = &sta->deflink.rx_stats; + int cpu; +@@ -2398,8 +2398,13 @@ sta_get_last_rx_stats(struct sta_info *s + + for_each_possible_cpu(cpu) { + struct ieee80211_sta_rx_stats *cpustats; ++ u16 rate; + + cpustats = per_cpu_ptr(sta->deflink.pcpu_rx_stats, cpu); ++ rate = READ_ONCE(cpustats->last_rate); ++ ++ if(!cpustats->last_rx || (is_rx_bitrate && (rate == STA_STATS_RATE_INVALID))) ++ continue; + + if (time_after(cpustats->last_rx, stats->last_rx)) + stats = cpustats; +@@ -2476,7 +2481,7 @@ static void sta_stats_decode_rate(struct + + static int sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) + { +- u32 rate = READ_ONCE(sta_get_last_rx_stats(sta)->last_rate); ++ u32 rate = READ_ONCE(sta_get_last_rx_stats(sta, true)->last_rate); + + if (rate == STA_STATS_RATE_INVALID) + return -EINVAL; +@@ -2576,7 +2581,7 @@ void sta_set_sinfo(struct sta_info *sta, + int i, ac, cpu; + struct ieee80211_sta_rx_stats *last_rxstats; + +- last_rxstats = sta_get_last_rx_stats(sta); ++ last_rxstats = sta_get_last_rx_stats(sta, false); + + sinfo->generation = sdata->local->sta_generation; + +@@ -2859,7 +2864,7 @@ u32 sta_get_expected_throughput(struct s + + unsigned long ieee80211_sta_last_active(struct sta_info *sta) + { +- struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta); ++ struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta, false); + + if (!sta->deflink.status_stats.last_ack || + time_after(stats->last_rx, sta->deflink.status_stats.last_ack)) diff --git a/package/kernel/mac80211/patches/nss/subsys/780-mac80211-Advertise-HW-checksum-offload-only-for-ethm.patch b/package/kernel/mac80211/patches/nss/subsys/780-mac80211-Advertise-HW-checksum-offload-only-for-ethm.patch new file mode 100644 index 00000000000000..6be28b6ac56663 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/780-mac80211-Advertise-HW-checksum-offload-only-for-ethm.patch @@ -0,0 +1,111 @@ +From d4ddaebe2132dbb169f78da3666b11a21f645ea0 Mon Sep 17 00:00:00 2001 +From: Tamizh Chelvam Raja +Date: Fri, 21 Apr 2023 12:28:21 +0530 +Subject: [PATCH] mac80211: Advertise HW checksum offload only for ethmode + +Upper(NSS/SFE) layer might remove checksum offset from a skb +for the net device which advertise HW checksum offload +feature. This would create an issue if any software encrypted +packet or for the netdev which don't support IEEE80211_OFFLOAD_*. +Avoid this by advertising the HW checksum offload feature +only for the netdev which supports IEEE80211_OFFLOAD_* +and have an check before checking checksum offset for the +exceptional packets getting called from 8023_xmit API. + +Signed-off-by: Tamizh Chelvam Raja +--- + net/mac80211/ieee80211_i.h | 3 ++- + net/mac80211/iface.c | 4 ++++ + net/mac80211/tdls.c | 2 +- + net/mac80211/tx.c | 19 ++++++++++--------- + 4 files changed, 17 insertions(+), 11 deletions(-) + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -37,6 +37,8 @@ + #include "wme.h" + #include "rate.h" + ++#define IS_HW_CSUM_NOT_ENABLED(dev) (!((dev)->features & NETIF_F_HW_CSUM)) ++ + static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, + struct net_device *dev, struct sta_info *sta, + struct ieee80211_key *key, struct sk_buff *skb, +@@ -3632,7 +3634,7 @@ ieee80211_sdata_netdev_features(struct i + } + + static struct sk_buff * +-ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features) ++ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features, struct net_device *dev) + { + if (skb_is_gso(skb)) { + struct sk_buff *segs; +@@ -3650,7 +3652,7 @@ ieee80211_tx_skb_fixup(struct sk_buff *s + if (skb_needs_linearize(skb, features) && __skb_linearize(skb)) + goto free; + +- if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ if (skb->ip_summed == CHECKSUM_PARTIAL && IS_HW_CSUM_NOT_ENABLED(dev)) { + int ofs = skb_checksum_start_offset(skb); + + if (skb->encapsulation) +@@ -3796,7 +3798,7 @@ static bool ieee80211_xmit_fast(struct i + memcpy(ð, skb->data, ETH_HLEN - 2); + + /* after this point (skb is modified) we cannot return false */ +- skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); ++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata), sdata->dev); + if (!skb) + return true; + +@@ -4344,7 +4346,7 @@ void __ieee80211_subif_start_xmit(struct + * things so we cannot really handle checksum or GSO offload. + * fix it up in software before we handle anything else. + */ +- skb = ieee80211_tx_skb_fixup(skb, 0); ++ skb = ieee80211_tx_skb_fixup(skb, 0, dev); + if (!skb) { + len = 0; + goto out; +@@ -4715,7 +4717,7 @@ static void ieee80211_8023_xmit(struct i + } + } + +- skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); ++ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata), dev); + if (!skb) + return; + +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -2267,6 +2267,10 @@ int ieee80211_if_add(struct ieee80211_lo + + ndev->features |= local->hw.netdev_features; + ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ++ if ((type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_STATION) && ++ ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) && !params->use_4addr) ++ ndev->features |= NETIF_F_HW_CSUM; ++ + ndev->hw_features |= ndev->features & + MAC80211_SUPPORTED_FEATURES_TX; + sdata->vif.netdev_features = local->hw.netdev_features; +--- a/net/mac80211/mesh.c ++++ b/net/mac80211/mesh.c +@@ -13,6 +13,8 @@ + #include "wme.h" + #include "driver-ops.h" + ++#define IS_HW_CSUM_NOT_ENABLED(dev) (!((dev)->features & NETIF_F_HW_CSUM)) ++ + static int mesh_allocated; + static struct kmem_cache *rm_cache; + +@@ -797,7 +799,7 @@ bool ieee80211_mesh_xmit_fast(struct iee + if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) + return false; + +- if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ if (skb->ip_summed == CHECKSUM_PARTIAL && IS_HW_CSUM_NOT_ENABLED(sdata->dev)) { + skb_set_transport_header(skb, skb_checksum_start_offset(skb)); + if (skb_checksum_help(skb)) + return false; diff --git a/package/kernel/mac80211/patches/nss/subsys/785-wifi-mac80211-Add-mac-hw-flag-to-avoid-queue-skb.patch b/package/kernel/mac80211/patches/nss/subsys/785-wifi-mac80211-Add-mac-hw-flag-to-avoid-queue-skb.patch new file mode 100644 index 00000000000000..c2846abf7e5961 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/785-wifi-mac80211-Add-mac-hw-flag-to-avoid-queue-skb.patch @@ -0,0 +1,342 @@ +From ca28b8b125c27063b9b4bc60bb85206ca8e0d403 Mon Sep 17 00:00:00 2001 +From: Yuvasree Sivasankaran +Date: Thu, 31 Aug 2023 10:59:33 +0530 +Subject: [PATCH] wifi: mac80211: Add mac hw flag to avoid queue skb + +Queue SKB in mac80211 become mandatory from latest 6.1 kernel. Because of +this queuing, there will be performance degradation. Add hw flag option +to enable tx queue in Driver/Hardware. + +Driver/hardware can register for HAS_TX_QUEUE HW flag and avoid tx queuing +in mac80211. + +Add same HW flag checks to avoid accessing skb queues which will be +NULL or invalid and also NULL checks for sta txqs for NULL or invalid +access. + +Signed-off-by: Yuvasree Sivasankaran +--- + include/net/mac80211.h | 1 + + net/mac80211/debugfs.c | 1 + + net/mac80211/tx.c | 18 ++++++++++++++---- + 4 files changed, 17 insertions(+), 4 deletions(-) + +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -2732,6 +2732,9 @@ struct ieee80211_txq { + * + * @IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD: Hardware suports tid calssification offload. + * ++ * @IEE80211_HW_HAS_TX_QUEUE: Hardware/drivers has tx queue, does skb queuing itself, ++ * the stack will not do tx queuing. ++ * + * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays + */ + enum ieee80211_hw_flags { +@@ -2792,6 +2795,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, + IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD, + IEEE80211_HW_SUPPORTS_TID_CLASS_OFFLOAD, ++ IEEE80211_HW_HAS_TX_QUEUE, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -508,6 +508,7 @@ static const char *hw_flag_names[] = { + FLAG(SUPPORTS_NSS_OFFLOAD), + FLAG(SUPPORTS_MESH_NSS_OFFLOAD), + FLAG(SUPPORTS_TID_CLASS_OFFLOAD), ++ FLAG(HAS_TX_QUEUE), + #undef FLAG + }; + +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -1603,6 +1603,9 @@ int ieee80211_txq_setup_flows(struct iee + bool supp_vht = false; + enum nl80211_band band; + ++ if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ return 0; ++ + ret = fq_init(fq, 4096); + if (ret) + return ret; +@@ -1650,6 +1653,9 @@ void ieee80211_txq_teardown_flows(struct + { + struct fq *fq = &local->fq; + ++ if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ return; ++ + kfree(local->cvars); + local->cvars = NULL; + +@@ -1666,7 +1672,8 @@ static bool ieee80211_queue_skb(struct i + struct ieee80211_vif *vif; + struct txq_info *txqi; + +- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) ++ if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE) || ++ sdata->vif.type == NL80211_IFTYPE_MONITOR) + return false; + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) +@@ -4329,7 +4336,8 @@ void __ieee80211_subif_start_xmit(struct + } + } + +- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); ++ if (unlikely(!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))) ++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + ieee80211_aggr_check(sdata, sta, skb); + + if (sta) { +@@ -4681,8 +4689,10 @@ static void ieee80211_8023_xmit(struct i + bool multicast; + u8 tid; + +- queue = ieee80211_select_queue(sdata, sta, skb); +- skb_set_queue_mapping(skb, queue); ++ if (unlikely(!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE))) { ++ queue = ieee80211_select_queue(sdata, sta, skb); ++ skb_set_queue_mapping(skb, queue); ++ } + + multicast = is_multicast_ether_addr(ra); + +@@ -6379,9 +6389,12 @@ int ieee80211_tx_control_port(struct wip + } + + if (!IS_ERR(sta)) { +- u16 queue = ieee80211_select_queue(sdata, sta, skb); + +- skb_set_queue_mapping(skb, queue); ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) { ++ u16 queue = ieee80211_select_queue(sdata, sta, skb); ++ ++ skb_set_queue_mapping(skb, queue); ++ } + + /* + * for MLO STA, the SA should be the AP MLD address, but +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -4524,6 +4524,9 @@ static int ieee80211_get_txq_stats(struc + struct ieee80211_sub_if_data *sdata; + int ret = 0; + ++ if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ return 1; ++ + spin_lock_bh(&local->fq.lock); + rcu_read_lock(); + +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -839,7 +839,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_ + atomic_set(&local->agg_queue_stop[i], 0); + } + tasklet_setup(&local->tx_pending_tasklet, ieee80211_tx_pending); +- tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); ++ ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ tasklet_setup(&local->wake_txqs_tasklet, ieee80211_wake_txqs); ++ + tasklet_setup(&local->tasklet, ieee80211_tasklet_handler); + + skb_queue_head_init(&local->skb_queue); +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -1549,6 +1549,9 @@ static void sta_ps_start(struct sta_info + + ieee80211_clear_fast_xmit(sta); + ++ if (!sta->sta.txq[0]) ++ return; ++ + for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { + struct ieee80211_txq *txq = sta->sta.txq[tid]; + struct txq_info *txqi = to_txq_info(txq); +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -140,15 +140,17 @@ static void __cleanup_single_sta(struct + atomic_dec(&ps->num_sta_ps); + } + +- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { +- struct txq_info *txqi; ++ if (sta->sta.txq[0]) { ++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { ++ struct txq_info *txqi; + +- if (!sta->sta.txq[i]) +- continue; ++ if (!sta->sta.txq[i]) ++ continue; + +- txqi = to_txq_info(sta->sta.txq[i]); ++ txqi = to_txq_info(sta->sta.txq[i]); + +- ieee80211_txq_purge(local, txqi); ++ ieee80211_txq_purge(local, txqi); ++ } + } + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { +@@ -430,7 +432,9 @@ void sta_info_free(struct ieee80211_loca + + sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); + +- kfree(to_txq_info(sta->sta.txq[0])); ++ if (sta->sta.txq[0]) ++ kfree(to_txq_info(sta->sta.txq[0])); ++ + kfree(rcu_dereference_raw(sta->sta.rates)); + #ifdef CPTCFG_MAC80211_MESH + kfree(sta->mesh); +@@ -532,8 +536,6 @@ __sta_info_alloc(struct ieee80211_sub_if + struct ieee80211_local *local = sdata->local; + struct ieee80211_hw *hw = &local->hw; + struct sta_info *sta; +- void *txq_data; +- int size; + int i; + + sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); +@@ -611,18 +613,22 @@ __sta_info_alloc(struct ieee80211_sub_if + + sta->last_connected = ktime_get_seconds(); + +- size = sizeof(struct txq_info) + +- ALIGN(hw->txq_data_size, sizeof(void *)); + +- txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); +- if (!txq_data) +- goto free; ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) { ++ void *txq_data; ++ int size = sizeof(struct txq_info) + ++ ALIGN(hw->txq_data_size, sizeof(void *)); ++ ++ txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); ++ if (!txq_data) ++ goto free; + +- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { +- struct txq_info *txq = txq_data + i * size; ++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { ++ struct txq_info *txq = txq_data + i * size; + +- /* might not do anything for the (bufferable) MMPDU TXQ */ +- ieee80211_txq_init(sdata, sta, txq, i); ++ /* might not do anything for the (bufferable) MMPDU TXQ */ ++ ieee80211_txq_init(sdata, sta, txq, i); ++ } + } + + if (sta_prepare_rate_control(local, sta, gfp)) +@@ -696,7 +702,8 @@ __sta_info_alloc(struct ieee80211_sub_if + return sta; + + free_txq: +- kfree(to_txq_info(sta->sta.txq[0])); ++ if (sta->sta.txq[0]) ++ kfree(to_txq_info(sta->sta.txq[0])); + free: + sta_info_free_link(&sta->deflink); + #ifdef CPTCFG_MAC80211_MESH +@@ -1691,11 +1698,13 @@ void ieee80211_sta_ps_deliver_wakeup(str + if (!ieee80211_hw_check(&local->hw, AP_LINK_PS)) + drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); + +- for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { +- if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i])) +- continue; ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) { ++ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { ++ if (!sta->sta.txq[i] || !txq_has_queue(sta->sta.txq[i])) ++ continue; + +- schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i])); ++ schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i])); ++ } + } + + skb_queue_head_init(&pending); +@@ -2110,6 +2119,9 @@ ieee80211_sta_ps_deliver_response(struct + * TIM recalculation. + */ + ++ if (!sta->sta.txq[0]) ++ return; ++ + for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { + if (!sta->sta.txq[tid] || + !(driver_release_tids & BIT(tid)) || +@@ -2546,7 +2558,7 @@ static void sta_set_tidstats(struct sta_ + tidstats->tx_msdu_failed = sta->deflink.status_stats.msdu_failed[tid]; + } + +- if (tid < IEEE80211_NUM_TIDS) { ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE) && tid < IEEE80211_NUM_TIDS) { + spin_lock_bh(&local->fq.lock); + rcu_read_lock(); + +@@ -2874,6 +2886,9 @@ unsigned long ieee80211_sta_last_active( + + static void sta_update_codel_params(struct sta_info *sta, u32 thr) + { ++ if (ieee80211_hw_check(&sta->sdata->local->hw, HAS_TX_QUEUE)) ++ return; ++ + if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) { + sta->cparams.target = MS2TIME(50); + sta->cparams.interval = MS2TIME(300); +--- a/net/mac80211/debugfs_sta.c ++++ b/net/mac80211/debugfs_sta.c +@@ -162,6 +162,9 @@ static ssize_t sta_aqm_read(struct file + bufsz + buf - p, + "tid ac backlog-bytes backlog-packets new-flows drops marks overlimit collisions tx-bytes tx-packets flags\n"); + ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ goto skip_txq_info; ++ + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + if (!sta->sta.txq[i]) + continue; +@@ -186,6 +189,7 @@ static ssize_t sta_aqm_read(struct file + test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : ""); + } + ++skip_txq_info: + rcu_read_unlock(); + spin_unlock_bh(&local->fq.lock); + +--- a/net/mac80211/mesh.c ++++ b/net/mac80211/mesh.c +@@ -832,7 +832,8 @@ bool ieee80211_mesh_xmit_fast(struct iee + if (!skb) + return true; + +- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); ++ if (unlikely(!ieee80211_hw_check(&sdata->local->hw, HAS_TX_QUEUE))) ++ skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); + + meshhdr = (struct ieee80211s_hdr *)entry->hdr; + if ((meshhdr->flags & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6) { +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -477,10 +477,8 @@ static void __ieee80211_wake_queue(struc + * release someone's lock, but it is fine because all the callers of + * __ieee80211_wake_queue call it right before releasing the lock. + */ +- if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) ++ if (!ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) + tasklet_schedule(&local->wake_txqs_tasklet); +- else +- _ieee80211_wake_txqs(local, flags); + } + + void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, diff --git a/package/kernel/mac80211/patches/nss/subsys/829-mac80211-fix-mesh-ping-issue.patch b/package/kernel/mac80211/patches/nss/subsys/829-mac80211-fix-mesh-ping-issue.patch new file mode 100644 index 00000000000000..642901bed17459 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/829-mac80211-fix-mesh-ping-issue.patch @@ -0,0 +1,113 @@ +From a76238143218ea348cec4b5d26fe9411338ae09e Mon Sep 17 00:00:00 2001 +From: Aaradhana Sahu +Date: Fri, 6 Oct 2023 18:19:53 +0530 +Subject: [PATCH] mac80211: fix mesh ping issue + +Signed-off-by: Aaradhana Sahu +--- + net/mac80211/rx.c | 68 +++-------------------------------------------- + 1 file changed, 4 insertions(+), 64 deletions(-) + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -4647,16 +4647,14 @@ void ieee80211_check_fast_rx(struct sta_ + + break; + case NL80211_IFTYPE_MESH_POINT: +- /* Not required for NSS mode */ +- if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) +- goto clear; + /* Note: da and sa offs are not static, determine in fast rx path */ + + fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_FROMDS | + IEEE80211_FCTL_TODS); +- +- fastrx.internal_forward = 0; +- break; ++ fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3); ++ fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr4); ++ break; ++ + default: + goto clear; + } +@@ -4878,10 +4876,7 @@ static bool ieee80211_invoke_fast_rx(str + u8 sa[ETH_ALEN]; + } addrs __aligned(2); + struct ieee80211_sta_rx_stats *stats; +- struct ieee80211s_hdr *mesh_hdr; +- struct mesh_path *mppath; + u8 da_offs = fast_rx->da_offs, sa_offs = fast_rx->sa_offs; +- struct ieee80211_sub_if_data *sdata = rx->sdata; + + /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write + * to a common data structure; drivers can implement that per queue +@@ -4931,37 +4926,6 @@ static bool ieee80211_invoke_fast_rx(str + snap_offs += IEEE80211_CCMP_HDR_LEN; + } + +- /* Find corresponding offsets for mesh hdr */ +- if (ieee80211_vif_is_mesh(&sdata->vif)) { +- if (status->rx_flags & IEEE80211_RX_AMSDU) +- return false; +- +- /* All mesh data frames needs to be QoS Data */ +- if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) +- return false; +- +- /* TODO forwarding not handled yet in fast rx */ +- if (!ether_addr_equal(fast_rx->vif_addr, hdr->addr3)) +- return false; +- +- /* Check if Min Mesh hdr is present */ +- if (!pskb_may_pull(skb, hdrlen + 6)) +- goto drop; +- +- /* Goto mesh hdr, located at snap offs compared to AP/STA */ +- mesh_hdr = (struct ieee80211s_hdr *) (skb->data + snap_offs); +- +- /* Only Ext Mesh hdr supported in this path now */ +- if ((mesh_hdr->flags & MESH_FLAGS_AE) != MESH_FLAGS_AE_A5_A6) +- return false; +- +- /* Point to eaddr1 and eaddr2 */ +- da_offs = snap_offs + ETH_ALEN; +- sa_offs = da_offs + ETH_ALEN; +- +- snap_offs += sizeof(struct ieee80211s_hdr); +- } +- + if (!ieee80211_vif_is_mesh(&rx->sdata->vif) && + !(status->rx_flags & IEEE80211_RX_AMSDU)) { + if (!pskb_may_pull(skb, snap_offs + sizeof(*payload))) +@@ -4999,30 +4963,6 @@ static bool ieee80211_invoke_fast_rx(str + return true; + } + +- /* Update MPP table for the received packet */ +- if (ieee80211_vif_is_mesh(&sdata->vif)) { +- char *proxied_addr, *mpp_addr; +- +- mpp_addr = hdr->addr4; +- proxied_addr = mesh_hdr->eaddr2; +- +- /* Update mpp for the SA */ +- rcu_read_lock(); +- mppath = mpp_path_lookup(sdata, proxied_addr); +- if (!mppath) { +- mpp_path_add(sdata, proxied_addr, mpp_addr); +- } else { +- spin_lock_bh(&mppath->state_lock); +- +- if (!ether_addr_equal(mppath->mpp, mpp_addr)) +- ether_addr_copy(mppath->mpp, mpp_addr); +- +- mppath->exp_time = jiffies; +- spin_unlock_bh(&mppath->state_lock); +- } +- rcu_read_unlock(); +- } +- + /* do the header conversion - first grab the addresses */ + ether_addr_copy(addrs.da, skb->data + da_offs); + ether_addr_copy(addrs.sa, skb->data + sa_offs); diff --git a/package/kernel/mac80211/patches/nss/subsys/902-wifi-mac80211-Fix-memory-corruption-during-mesh-beac.patch b/package/kernel/mac80211/patches/nss/subsys/902-wifi-mac80211-Fix-memory-corruption-during-mesh-beac.patch new file mode 100644 index 00000000000000..0f8289a892d4a4 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/902-wifi-mac80211-Fix-memory-corruption-during-mesh-beac.patch @@ -0,0 +1,68 @@ +From 0ff455d1d446e34ae6c2596d4d8491f66fc61913 Mon Sep 17 00:00:00 2001 +From: Manish Dharanenthiran +Date: Sat, 2 Dec 2023 03:38:31 +0530 +Subject: [PATCH] wifi: mac80211: Fix memory corruption during mesh beacon + update + +During mesh beacon update, u64 flag is used to check for +bit set/unset for validation purpose. But, in +'ieee80211_mbss_info_change_notify' API, unsigned long flag +is modified using sizeof(u64). This leads to following issue: + + > 'mbss_changed' flag in 'ieee80211_if_mesh' is declared as + unsigned_long which creates an architecture dependency + (32bit vs 64bit) while modifying it with u64 flag which + leads to memory corruption. + +Fix above mentioned issue by replacing unsigned long with u64 +for changed flag. + +Signed-off-by: Manish Dharanenthiran +--- + net/mac80211/ieee80211_i.h | 2 +- + net/mac80211/mesh.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -682,7 +682,7 @@ struct ieee80211_if_mesh { + struct timer_list mesh_path_root_timer; + + unsigned long wrkq_flags; +- unsigned long mbss_changed[64 / BITS_PER_LONG]; ++ unsigned long mbss_changed; + + bool userspace_handles_dfs; + +--- a/net/mac80211/mesh.c ++++ b/net/mac80211/mesh.c +@@ -1184,7 +1184,7 @@ void ieee80211_mbss_info_change_notify(s + + /* if we race with running work, worst case this work becomes a noop */ + for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) +- set_bit(bit, ifmsh->mbss_changed); ++ set_bit(bit, &ifmsh->mbss_changed); + set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); + wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); + } +@@ -1266,7 +1266,7 @@ void ieee80211_stop_mesh(struct ieee8021 + + /* clear any mesh work (for next join) we may have accrued */ + ifmsh->wrkq_flags = 0; +- memset(ifmsh->mbss_changed, 0, sizeof(ifmsh->mbss_changed)); ++ ifmsh->mbss_changed = 0; + + local->fif_other_bss--; + atomic_dec(&local->iff_allmultis); +@@ -1733,9 +1733,9 @@ static void mesh_bss_info_changed(struct + u32 bit; + u64 changed = 0; + +- for_each_set_bit(bit, ifmsh->mbss_changed, ++ for_each_set_bit(bit, &ifmsh->mbss_changed, + sizeof(changed) * BITS_PER_BYTE) { +- clear_bit(bit, ifmsh->mbss_changed); ++ clear_bit(bit, &ifmsh->mbss_changed); + changed |= BIT(bit); + } + diff --git a/package/kernel/nat46/Makefile b/package/kernel/nat46/Makefile index 296ef5a058e83e..dd1d55b84eef02 100644 --- a/package/kernel/nat46/Makefile +++ b/package/kernel/nat46/Makefile @@ -12,6 +12,8 @@ PKG_SOURCE_VERSION:=4c5beee236841724219598fabb1edc93d4f08ce5 PKG_MAINTAINER:=Hans Dedecker PKG_LICENSE:=GPL-2.0 +PKG_BUILD_PARALLEL:=1 + include $(INCLUDE_DIR)/package.mk define KernelPackage/nat46 @@ -25,11 +27,17 @@ endef include $(INCLUDE_DIR)/kernel-defaults.mk +define Build/InstallDev + $(INSTALL_DIR) $(STAGING_DIR)/usr/include/nat46 + $(INSTALL_DATA) $(PKG_BUILD_DIR)/nat46/modules/*.h $(STAGING_DIR)/usr/include/nat46/ +endef + define Build/Compile - $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/nat46/modules" \ + +$(KERNEL_MAKE) M="$(PKG_BUILD_DIR)/nat46/modules" \ MODFLAGS="-DMODULE -mlong-calls" \ EXTRA_CFLAGS="-DNAT46_VERSION=\\\"$(PKG_SOURCE_VERSION)\\\"" \ modules + $(INSTALL_DATA) $(PKG_BUILD_DIR)/nat46/modules/Module.symvers $(PKG_BUILD_DIR)/Module.symvers endef $(eval $(call KernelPackage,nat46)) diff --git a/package/kernel/nat46/patches/101-skb-reset.patch b/package/kernel/nat46/patches/101-skb-reset.patch new file mode 100644 index 00000000000000..14cf2d75a07d88 --- /dev/null +++ b/package/kernel/nat46/patches/101-skb-reset.patch @@ -0,0 +1,30 @@ +Author: Pavithra R +Date: Sun Sep 20 13:33:42 2020 +0530 + +nat46: Add skb_ext_reset to reset skb extensions + +This patch adds support to reset the skb extensions before +resetting the netfilter. Without the change, conntrack +is in invalid state and traffic gets dropped. + +Change-Id: I24ee6fe8a9a9dec09d61d8e716fff587f65e4e4f +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1710,6 +1710,7 @@ int nat46_ipv6_input(struct sk_buff *old + #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) + nf_reset(new_skb); + #else ++ skb_ext_reset(new_skb); + nf_reset_ct(new_skb); + #endif + +@@ -1936,6 +1937,7 @@ int nat46_ipv4_input(struct sk_buff *old + #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) + nf_reset(new_skb); + #else ++ skb_ext_reset(new_skb); + nf_reset_ct(new_skb); + #endif + diff --git a/package/kernel/nat46/patches/102-mapt.patch b/package/kernel/nat46/patches/102-mapt.patch new file mode 100644 index 00000000000000..1e83481c967f8b --- /dev/null +++ b/package/kernel/nat46/patches/102-mapt.patch @@ -0,0 +1,209 @@ +Author: Pavithra R +Date: Sat Aug 1 13:27:20 2020 +0530 + +nat46: Export APIs for acceleration engine support in nat46 for kernel 5.4 + +This patch is propagated from kernel 4.4 commit +861e64a607fd22d5af089cf56539f42a2e31d581 + +The patch defines and exports APIs in nat46 to be used for accelaration. + +Change-Id: I7934b15544953f870d3595b8b359433b4fff7c30 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1497,7 +1497,6 @@ static uint16_t nat46_fixup_icmp_dest_un + return 0; + } + +- + /* Fixup ICMP->ICMP6 before IP header translation, according to http://tools.ietf.org/html/rfc6145 */ + + static uint16_t nat46_fixup_icmp(nat46_instance_t *nat46, struct iphdr *iph, struct sk_buff *old_skb) { +@@ -1579,6 +1578,10 @@ int pairs_xlate_v6_to_v4_outer(nat46_ins + return ( (xlate_src >= 0) && (xlate_dst >= 0) ); + } + ++int xlate_6_to_4(struct net_device *dev, struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { ++ return pairs_xlate_v6_to_v4_outer(netdev_nat46_instance(dev), ip6h, proto, pv4saddr, pv4daddr); ++} ++EXPORT_SYMBOL(xlate_6_to_4); + + int nat46_ipv6_input(struct sk_buff *old_skb) { + struct ipv6hdr *ip6h = ipv6_hdr(old_skb); +@@ -1733,6 +1736,10 @@ int nat46_ipv6_input(struct sk_buff *old + + nat46debug(5, "about to send v4 packet, flags: %02x", IPCB(new_skb)->flags); + nat46_netdev_count_xmit(new_skb, old_skb->dev); ++ ++ /* set skb->iif */ ++ new_skb->skb_iif = old_skb->skb_iif; ++ + netif_rx(new_skb); + + /* TBD: should copy be released here? */ +@@ -1841,6 +1848,10 @@ int pairs_xlate_v4_to_v6_outer(nat46_ins + return 0; + } + ++int xlate_4_to_6(struct net_device *dev, struct iphdr *hdr4, uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr) { ++ return pairs_xlate_v4_to_v6_outer(netdev_nat46_instance(dev), hdr4, &sport, &dport, v6saddr, v6daddr); ++} ++EXPORT_SYMBOL(xlate_4_to_6); + + int nat46_ipv4_input(struct sk_buff *old_skb) { + nat46_instance_t *nat46 = get_nat46_instance(old_skb); +@@ -1981,6 +1992,10 @@ int nat46_ipv4_input(struct sk_buff *old + + nat46debug(5, "about to send v6 packet, flags: %02x", IP6CB(new_skb)->flags); + nat46_netdev_count_xmit(new_skb, old_skb->dev); ++ ++ /* set skb->iif */ ++ new_skb->skb_iif = old_skb->skb_iif; ++ + netif_rx(new_skb); + + done: +@@ -1988,4 +2003,22 @@ done: + return err; + } + ++int nat46_get_npairs(struct net_device *dev) { ++ nat46_instance_t *nat46 = netdev_nat46_instance(dev); ++ return nat46->npairs; ++} ++EXPORT_SYMBOL(nat46_get_npairs); + ++bool nat46_get_rule_config(struct net_device *dev, nat46_xlate_rulepair_t **nat46_rule_pair, int *count) { ++ nat46_instance_t *nat46 = netdev_nat46_instance(dev); ++ if (nat46->npairs < 1) { ++ /* ++ * no rules ? ++ */ ++ return false; ++ } ++ *count = nat46->npairs; ++ *nat46_rule_pair = nat46->pairs; ++ return true; ++} ++EXPORT_SYMBOL(nat46_get_rule_config); +--- a/nat46/modules/nat46-core.h ++++ b/nat46/modules/nat46-core.h +@@ -42,18 +42,18 @@ typedef enum { + #define NAT46_SIGNATURE 0x544e3634 + #define FREED_NAT46_SIGNATURE 0xdead544e + +-typedef struct { ++typedef struct nat46_xlate_rule { + nat46_xlate_style_t style; + struct in6_addr v6_pref; +- int v6_pref_len; +- u32 v4_pref; +- int v4_pref_len; +- int ea_len; +- int psid_offset; +- int fmr_flag; ++ int v6_pref_len; ++ u32 v4_pref; ++ int v4_pref_len; ++ int ea_len; ++ int psid_offset; ++ int fmr_flag; + } nat46_xlate_rule_t; + +-typedef struct { ++typedef struct nat46_xlate_rulepair { + nat46_xlate_rule_t local; + nat46_xlate_rule_t remote; + } nat46_xlate_rulepair_t; +@@ -82,4 +82,9 @@ nat46_instance_t *get_nat46_instance(str + nat46_instance_t *alloc_nat46_instance(int npairs, nat46_instance_t *old, int from_ipair, int to_ipair, int remove_ipair); + void release_nat46_instance(nat46_instance_t *nat46); + ++int xlate_6_to_4(struct net_device *dev, struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr); ++int xlate_4_to_6(struct net_device *dev, struct iphdr *hdr4, uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr); ++bool nat46_get_rule_config(struct net_device *dev, nat46_xlate_rulepair_t **nat46_rule_pair, int *count); ++int nat46_get_npairs(struct net_device *dev); ++ + #endif +--- a/nat46/modules/nat46-netdev.c ++++ b/nat46/modules/nat46-netdev.c +@@ -24,10 +24,12 @@ + #include + #include + #include ++#include + #include "nat46-core.h" + #include "nat46-module.h" + + #define NETDEV_DEFAULT_NAME "nat46." ++static RADIX_TREE(netdev_tree, GFP_ATOMIC); + + typedef struct { + u32 sig; +@@ -83,6 +85,18 @@ void nat46_netdev_count_xmit(struct sk_b + dev->stats.tx_bytes += skb->len; + } + ++void nat46_update_stats(struct net_device *dev, uint32_t rx_packets, uint32_t rx_bytes, ++ uint32_t tx_packets, uint32_t tx_bytes, uint32_t rx_dropped, uint32_t tx_dropped) ++{ ++ dev->stats.rx_packets += rx_packets; ++ dev->stats.rx_bytes += rx_bytes; ++ dev->stats.tx_packets += tx_packets; ++ dev->stats.tx_bytes += tx_bytes; ++ dev->stats.rx_dropped += rx_dropped; ++ dev->stats.tx_dropped += tx_dropped; ++} ++EXPORT_SYMBOL(nat46_update_stats); ++ + void *netdev_nat46_instance(struct net_device *dev) { + nat46_netdev_priv_t *priv = netdev_priv(dev); + return priv->nat46; +@@ -159,6 +173,11 @@ int nat46_netdev_create(char *basename, + printk("nat46: netdevice nat46 '%s' created successfully.\n", devname); + kfree(devname); + ++ /* ++ * add this netdevice to list ++ */ ++ radix_tree_insert(&netdev_tree, (*dev)->ifindex, (void *)*dev); ++ + return 0; + + err_register_dev: +@@ -175,10 +194,24 @@ void nat46_netdev_destroy(struct net_dev + netif_stop_queue(dev); + netdev_nat46_set_instance(dev, NULL); + unregister_netdev(dev); ++ radix_tree_delete(&netdev_tree, dev->ifindex); + free_netdev(dev); + printk("nat46: Destroying nat46 device.\n"); + } + ++bool is_map_t_dev(struct net_device *dev) ++{ ++ if(!dev) { ++ return false; ++ } ++ ++ if(radix_tree_lookup(&netdev_tree, dev->ifindex)) { ++ return true; ++ } ++ return false; ++} ++EXPORT_SYMBOL(is_map_t_dev); ++ + static int is_nat46(struct net_device *dev) { + nat46_netdev_priv_t *priv = netdev_priv(dev); + return (priv && (NAT46_DEVICE_SIGNATURE == priv->sig)); +--- a/nat46/modules/nat46-netdev.h ++++ b/nat46/modules/nat46-netdev.h +@@ -26,3 +26,6 @@ void nat64_show_all_configs(struct seq_f + void nat46_netdev_count_xmit(struct sk_buff *skb, struct net_device *dev); + void *netdev_nat46_instance(struct net_device *dev); + ++void nat46_update_stats(struct net_device *dev, uint32_t rx_packets, uint32_t rx_bytes, uint32_t tx_packets, uint32_t tx_bytes, ++ uint32_t rx_dropped, uint32_t tx_dropped); ++bool is_map_t_dev(struct net_device *dev); diff --git a/package/kernel/nat46/patches/103-tos.patch b/package/kernel/nat46/patches/103-tos.patch new file mode 100644 index 00000000000000..60ffcb2fae802a --- /dev/null +++ b/package/kernel/nat46/patches/103-tos.patch @@ -0,0 +1,39 @@ +Author: Pavithra R +Date: Sat Aug 1 13:55:33 2020 +0530 + +nat46: Set IPv6 traffic class from IPv4 ToS value + +Set IPv6 traffic class from IPv4 ToS value during +IPv4 to IPv6 translation and vice-versa. + +This patch is propagated from kernel 4.4 commit +1cd3b55b059d4513649bb73bc69da931ed3beb7b + +Change-Id: Ia14e53447e829c8648c01656237ac902ad8674ec +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -886,11 +886,12 @@ void *get_next_header_ptr6(void *pv6, in + } + + void fill_v4hdr_from_v6hdr(struct iphdr * iph, struct ipv6hdr *ip6h, __u32 v4saddr, __u32 v4daddr, __u16 id, __u16 frag_off, __u16 proto, int l3_payload_len) { ++ uint32_t ver_class_flow = ntohl(*(__be32 *)ip6h); + iph->ttl = ip6h->hop_limit; + iph->saddr = v4saddr; + iph->daddr = v4daddr; + iph->protocol = proto; +- *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (0x00/*tos*/ & 0xff)); ++ *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | ((ver_class_flow >> 20) & 0xff)); + iph->frag_off = frag_off; + iph->id = id; + iph->tot_len = htons( l3_payload_len + IPV4HDRSIZE ); +@@ -1859,7 +1860,7 @@ int nat46_ipv4_input(struct sk_buff *old + uint16_t sport = 0, dport = 0; + + int err = 0; +- int tclass = 0; ++ uint8_t tclass = 0; + int flowlabel = 0; + int check_for_l4 = 0; + int having_l4 = 0; diff --git a/package/kernel/nat46/patches/104-icmp.patch b/package/kernel/nat46/patches/104-icmp.patch new file mode 100644 index 00000000000000..7907a66726005b --- /dev/null +++ b/package/kernel/nat46/patches/104-icmp.patch @@ -0,0 +1,439 @@ +Author: Pavithra R +Date: Mon Aug 3 17:03:37 2020 +0530 + +nat46: Fix for icmp translation issues. + +This patch is propagated from kernel 4.4 commit +45fce10ba0105515289930b3e3f9df57bf3c22b6. + +Fixed icmpv4 to icmpv6 and vice-versa translation issues, in accordance with RFC6145. + +The change covers: +1. Translation of ICMP errors from IPv4 to IPv6 and vice-versa. +2. Translation of inner L3 packet header {Eth:IPv4:ICMP:IPv4:ICMP} in ICMP error messages. +3. Address translation for packets not having port numbers, hence CE/BR needs to fetch this + information from inner header (atleast 28 bytes (IP hdr + 8 bytes) of orignal packet received + that is transmitted back will be there in response). + +Change-Id: I677474728aeaee656376fdb1edcb9476783d5b40 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -29,6 +29,9 @@ + #include "nat46-core.h" + #include "nat46-module.h" + ++static uint16_t xlate_pkt_in_err_v4_to_v6(nat46_instance_t *nat46, struct iphdr *iph, ++ struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport); ++ + void + nat46debug_dump(nat46_instance_t *nat46, int level, void *addr, int len) + { +@@ -885,6 +888,14 @@ void *get_next_header_ptr6(void *pv6, in + return ret; + } + ++void fill_v6hdr_from_v4hdr(struct iphdr *iph, struct ipv6hdr *ip6h) { ++ *((__be16 *)ip6h) = htons((6 << 12) | (iph->tos << 4)); /* Version, Traffic Class */ ++ memset(&(ip6h->flow_lbl), 0, sizeof(ip6h->flow_lbl)); /* Flowlabel */ ++ ip6h->payload_len = htons(ntohs(iph->tot_len) - IPV4HDRSIZE); ++ ip6h->nexthdr = iph->protocol; ++ ip6h->hop_limit = iph->ttl; ++} ++ + void fill_v4hdr_from_v6hdr(struct iphdr * iph, struct ipv6hdr *ip6h, __u32 v4saddr, __u32 v4daddr, __u16 id, __u16 frag_off, __u16 proto, int l3_payload_len) { + uint32_t ver_class_flow = ntohl(*(__be32 *)ip6h); + iph->ttl = ip6h->hop_limit; +@@ -1212,10 +1223,14 @@ static void nat46_fixup_icmp6_paramprob( + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, -1 }; + u32 *pptr6 = icmp6_parameter_ptr(icmp6h); + u8 *pptr4 = icmp_parameter_ptr((struct icmphdr *)icmp6h); +- int new_pptr = -1; ++ u8 new_pptr = -1; + int len = ntohs(ip6h->payload_len)-sizeof(*icmp6h); + + switch(icmp6h->icmp6_code) { ++ case 1: ++ update_icmp6_type_code(nat46, icmp6h, 3, 2); ++ break; ++ + case 0: + if(*pptr6 < sizeof(ptr6_4)/sizeof(ptr6_4[0])) { + new_pptr = ptr6_4[*pptr6]; +@@ -1224,27 +1239,21 @@ static void nat46_fixup_icmp6_paramprob( + *pptr4 = 0xff & new_pptr; + update_icmp6_type_code(nat46, icmp6h, 12, 0); + len = xlate_payload6_to4(nat46, (icmp6h + 1), get_next_header_ptr6((icmp6h + 1), len), len, &icmp6h->icmp6_cksum, ptailTruncSize); +- } else { +- ip6h->nexthdr = NEXTHDR_NONE; ++ update_icmp6_type_code(nat46, icmp6h, 12, 0); ++ break; + } +- } else { +- ip6h->nexthdr = NEXTHDR_NONE; + } +- break; +- case 1: +- icmp6h->icmp6_cksum = csum16_upd(icmp6h->icmp6_cksum, ((*pptr6 >> 16) & 0xffff), 0); +- icmp6h->icmp6_cksum = csum16_upd(icmp6h->icmp6_cksum, (*pptr6 & 0xffff), 0); +- *pptr6 = 0; +- update_icmp6_type_code(nat46, icmp6h, 3, 2); +- len = xlate_payload6_to4(nat46, (icmp6h + 1), get_next_header_ptr6((icmp6h + 1), len), len, &icmp6h->icmp6_cksum, ptailTruncSize); +- break; ++#if __has_attribute(__fallthrough__) ++ __attribute__((__fallthrough__)); ++#endif + case 2: /* fallthrough to default */ + default: + ip6h->nexthdr = NEXTHDR_NONE; ++ return; + } ++ len = xlate_payload6_to4(nat46, (icmp6h + 1), get_next_header_ptr6((icmp6h + 1), len), len, &icmp6h->icmp6_cksum, ptailTruncSize); + } + +- + /* Fixup ICMP6->ICMP before IP header translation, according to http://tools.ietf.org/html/rfc6145 */ + + static void nat46_fixup_icmp6(nat46_instance_t *nat46, struct ipv6hdr *ip6h, struct icmp6hdr *icmp6h, struct sk_buff *old_skb, int *ptailTruncSize) { +@@ -1299,17 +1308,19 @@ int ip6_input_not_interested(nat46_insta + return 0; + } + +-static uint16_t nat46_fixup_icmp_time_exceeded(nat46_instance_t *nat46, struct iphdr *iph, struct icmphdr *icmph, struct sk_buff *old_skb) { ++static uint16_t nat46_fixup_icmp_time_exceeded(nat46_instance_t *nat46, struct iphdr *iph, ++ struct icmphdr *icmph, struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport) { + /* + * Set the Type to 3, and adjust the + * ICMP checksum both to take the type change into account and + * to include the ICMPv6 pseudo-header. The Code is unchanged. + */ + icmph->type = 3; +- return 0; ++ return xlate_pkt_in_err_v4_to_v6(nat46, iph, old_skb, sport, dport); + } + +-static uint16_t nat46_fixup_icmp_parameterprob(nat46_instance_t *nat46, struct iphdr *iph, struct icmphdr *icmph, struct sk_buff *old_skb) { ++static uint16_t nat46_fixup_icmp_parameterprob(nat46_instance_t *nat46, struct iphdr *iph, ++ struct icmphdr *icmph, struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport) { + /* + * Set the Type to 4, and adjust the + * ICMP checksum both to take the type/code change into account +@@ -1352,27 +1363,33 @@ static uint16_t nat46_fixup_icmp_paramet + */ + static int ptr4_6[] = { 0, 1, 4, 4, -1, -1, -1, -1, 7, 6, -1, -1, 8, 8, 8, 8, 24, 24, 24, 24, -1 }; + u8 *icmp_pptr = icmp_parameter_ptr(icmph); +- int new_pptr = -1; ++ u32 *icmp6_pptr = icmp6_parameter_ptr((struct icmp6hdr *)icmph); ++ int8_t new_pptr = -1; ++ ++ icmph->type = 4; ++ + switch (icmph->code) { + case 0: + case 2: + if (*icmp_pptr < (sizeof(ptr4_6)/sizeof(ptr4_6[0]))) { + icmph->code = 0; + new_pptr = ptr4_6[*icmp_pptr]; +- if(new_pptr >= 0) { +- /* FIXME: update the parameter pointer in ICMPv6 with new_pptr value */ ++ if (new_pptr >= 0) { ++ *icmp6_pptr = new_pptr; ++ return xlate_pkt_in_err_v4_to_v6(nat46, iph, old_skb, sport, dport); + } +- } else { +- iph->protocol = NEXTHDR_NONE; + } +- break; ++#if __has_attribute(__fallthrough__) ++ __attribute__((__fallthrough__)); ++#endif + default: + iph->protocol = NEXTHDR_NONE; + } + return 0; + } + +-static uint16_t nat46_fixup_icmp_dest_unreach(nat46_instance_t *nat46, struct iphdr *iph, struct icmphdr *icmph, struct sk_buff *old_skb) { ++static uint16_t nat46_fixup_icmp_dest_unreach(nat46_instance_t *nat46, struct iphdr *iph, ++ struct icmphdr *icmph, struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport) { + /* + * Translate the Code as + * described below, set the Type to 1, and adjust the ICMP +@@ -1435,16 +1452,21 @@ static uint16_t nat46_fixup_icmp_dest_un + + u16 *pmtu = ((u16 *)icmph) + 3; /* IPv4-compatible MTU value is 16 bit */ + ++ icmph->type = 1; ++ + switch (icmph->code) { + case 0: + case 1: + icmph->code = 0; + break; +- case 2: +- /* FIXME: set ICMPv6 parameter pointer to 6 */ ++ case 2: { ++ u32 *icmp6_pptr = icmp6_parameter_ptr((struct icmp6hdr *)icmph); ++ *icmp6_pptr = 6; /* Offset to Next Proto field in IPv6 header. */ + icmph->type = 4; + icmph->code = 1; ++ nat46debug(3, "ICMP Proto Unreachable translated into IPv6 Param Prob.\n"); + break; ++ } + case 3: + icmph->code = 4; + break; +@@ -1494,13 +1516,15 @@ static uint16_t nat46_fixup_icmp_dest_un + break; + default: + iph->protocol = NEXTHDR_NONE; ++ return 0; + } +- return 0; ++ return xlate_pkt_in_err_v4_to_v6(nat46, iph, old_skb, sport, dport); + } + + /* Fixup ICMP->ICMP6 before IP header translation, according to http://tools.ietf.org/html/rfc6145 */ +- +-static uint16_t nat46_fixup_icmp(nat46_instance_t *nat46, struct iphdr *iph, struct sk_buff *old_skb) { ++static uint16_t nat46_fixup_icmp(nat46_instance_t *nat46, struct iphdr *iph, ++ struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport) ++{ + struct icmphdr *icmph = (struct icmphdr *)(iph+1); + uint16_t ret = 0; + +@@ -1509,22 +1533,22 @@ static uint16_t nat46_fixup_icmp(nat46_i + switch(icmph->type) { + case ICMP_ECHO: + icmph->type = ICMPV6_ECHO_REQUEST; +- ret = icmph->un.echo.id; ++ *sport = *dport = icmph->un.echo.id; + nat46debug(3, "ICMP echo request translated into IPv6, id: %d", ntohs(ret)); + break; + case ICMP_ECHOREPLY: + icmph->type = ICMPV6_ECHO_REPLY; +- ret = icmph->un.echo.id; ++ *sport = *dport = icmph->un.echo.id; + nat46debug(3, "ICMP echo reply translated into IPv6, id: %d", ntohs(ret)); + break; + case ICMP_TIME_EXCEEDED: +- ret = nat46_fixup_icmp_time_exceeded(nat46, iph, icmph, old_skb); ++ ret = nat46_fixup_icmp_time_exceeded(nat46, iph, icmph, old_skb, sport, dport); + break; + case ICMP_PARAMETERPROB: +- ret = nat46_fixup_icmp_parameterprob(nat46, iph, icmph, old_skb); ++ ret = nat46_fixup_icmp_parameterprob(nat46, iph, icmph, old_skb, sport, dport); + break; + case ICMP_DEST_UNREACH: +- ret = nat46_fixup_icmp_dest_unreach(nat46, iph, icmph, old_skb); ++ ret = nat46_fixup_icmp_dest_unreach(nat46, iph, icmph, old_skb, sport, dport); + break; + default: + /* Silently drop. */ +@@ -1544,11 +1568,13 @@ int pairs_xlate_v6_to_v4_outer(nat46_ins + + if(-1 == xlate_dst) { + if (xlate_v6_to_v4(nat46, &apair->local, &ip6h->daddr, pv4daddr)) { ++ nat46debug(5, "Dst addr %pI6 to %pI4 \n", &ip6h->daddr, pv4daddr); + xlate_dst = ipair; + } + } + if(-1 == xlate_src) { + if (xlate_v6_to_v4(nat46, &apair->remote, &ip6h->saddr, pv4saddr)) { ++ nat46debug(5, "Src addr %pI6 to %pI4 \n", &ip6h->saddr, pv4saddr); + xlate_src = ipair; + } + } +@@ -1659,6 +1685,7 @@ int nat46_ipv6_input(struct sk_buff *old + } + + if(!pairs_xlate_v6_to_v4_outer(nat46, ip6h, proto, &v4saddr, &v4daddr)) { ++ nat46debug(0, "[nat46] Could not translate v6->v4"); + goto done; + } + +@@ -1821,11 +1848,13 @@ int pairs_xlate_v4_to_v6_outer(nat46_ins + + if(-1 == xlate_src) { + if(xlate_v4_to_v6(nat46, &apair->local, &hdr4->saddr, v6saddr, sport)) { ++ nat46debug(5, "Src addr %pI4 to %pI6 \n", &hdr4->saddr, v6saddr); + xlate_src = ipair; + } + } + if(-1 == xlate_dst) { + if(xlate_v4_to_v6(nat46, &apair->remote, &hdr4->daddr, v6daddr, dport)) { ++ nat46debug(5, "Dst addr %pI4 to %pI6 \n", &hdr4->daddr, v6daddr); + xlate_dst = ipair; + } + } +@@ -1854,10 +1883,145 @@ int xlate_4_to_6(struct net_device *dev, + } + EXPORT_SYMBOL(xlate_4_to_6); + ++/* FIXME: This is a workaround, till the LPM is not added. The sport & dport in inner header will be dport & sport of the outer ++ * header, respectively. Hence, dest. and source ips of inner header will be found in local & remote rules, respectively. ++ * Will work only for a pair of local & remote rules. Once LPM is brought in, this method can be removed and ++ * pairs_xlate_v4_to_v6_outer be used instead. ++ */ ++int pairs_xlate_v4_to_v6_inner(nat46_instance_t *nat46, struct iphdr *iph, ++ uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr) { ++ int ipair = 0; ++ nat46_xlate_rulepair_t *apair = NULL; ++ int xlate_src = -1; ++ int xlate_dst = -1; ++ ++ for (ipair = 0; ipair < nat46->npairs; ipair++) { ++ apair = &nat46->pairs[ipair]; ++ ++ if (-1 == xlate_dst) { ++ if (xlate_v4_to_v6(nat46, &apair->local, &iph->daddr, v6daddr, &dport)) { ++ nat46debug(3, "Dst addr %pI4 to %pI6 \n", &iph->daddr, v6daddr); ++ xlate_dst = ipair; ++ } ++ } ++ if (-1 == xlate_src) { ++ if(xlate_v4_to_v6(nat46, &apair->remote, &iph->saddr, v6saddr, &sport)) { ++ nat46debug(3, "Src addr %pI4 to %pI6 \n", &iph->saddr, v6saddr); ++ xlate_src = ipair; ++ } ++ } ++ if ((xlate_src >= 0) && (xlate_dst >= 0)) { ++ /* we did manage to translate it */ ++ nat46debug(5, "[nat46] Inner header xlate results: src %d dst %d", xlate_src, xlate_dst); ++ return 1; ++ } else { ++ /* We did not match fully and there are more rules */ ++ if((ipair+1 < nat46->npairs) && is_last_pair_in_group(apair)) { ++ xlate_src = -1; ++ xlate_dst = -1; ++ } ++ } ++} ++ ++ nat46debug(1, "[nat46] Could not find a translation pair v4->v6"); ++ return 0; ++} ++ ++static uint16_t xlate_pkt_in_err_v4_to_v6(nat46_instance_t *nat46, struct iphdr *iph, ++ struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport) { ++ struct ipv6hdr ip6h; ++ char v6saddr[16], v6daddr[16]; ++ uint16_t temp_port = 0; ++ int ret = 0; ++ struct icmphdr *icmph = (struct icmphdr *)(iph + 1); ++ struct iphdr *iiph = (struct iphdr *)(icmph + 1); ++ ++ switch (iiph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr *th = (struct tcphdr *)(iiph + 1); ++ *sport = th->source; ++ *dport = th->dest; ++ iiph->protocol = NEXTHDR_TCP; ++ break; ++ } ++ case IPPROTO_UDP: { ++ struct udphdr *udp = (struct udphdr *)(iiph + 1); ++ *sport = udp->source; ++ *dport = udp->dest; ++ iiph->protocol = NEXTHDR_UDP; ++ break; ++ } ++ case IPPROTO_ICMP: { ++ struct icmphdr *icmph = (struct icmphdr *)(iiph + 1); ++ iiph->protocol = NEXTHDR_ICMP; ++ switch (icmph->type) { ++ case ICMP_ECHO: ++ icmph->type = ICMPV6_ECHO_REQUEST; ++ *sport = *dport = icmph->un.echo.id; ++ break; ++ case ICMP_ECHOREPLY: ++ icmph->type = ICMPV6_ECHO_REPLY; ++ *sport = *dport = icmph->un.echo.id; ++ break; ++ default: ++ nat46debug(3, "ICMP Error message can't be inside another ICMP Error messgae."); ++ *sport = *dport = 0; ++ return 0; ++ } ++ break; ++ } ++ default: ++ nat46debug(3, "[ICMPv4] Next header: %u. Only TCP, UDP, and ICMP are supported.", iiph->protocol); ++ *sport = *dport = 0; ++ return 0; ++ } ++ ++ nat46debug(3, "Retrieved from pkt in error: dest port %d, and src port %d.", ntohs(*dport), ntohs(*sport)); ++ ++ if (!pairs_xlate_v4_to_v6_inner(nat46, iiph, *sport, *dport, v6saddr, v6daddr)) { ++ nat46debug(0, "[nat46] Could not translate inner header v4->v6"); ++ *sport = *dport = 0; ++ return 0; ++ } ++ ++ fill_v6hdr_from_v4hdr (iiph, &ip6h); ++ memcpy(&ip6h.saddr, v6saddr, sizeof(ip6h.saddr)); ++ memcpy(&ip6h.daddr, v6daddr, sizeof(ip6h.daddr)); ++ ++ if (skb_tailroom(old_skb) >= IPV6V4HDRDELTA){ ++ skb_put(old_skb, IPV6V4HDRDELTA); ++ memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ntohs(iiph->tot_len) - IPV4HDRSIZE); ++ memcpy(iiph, &ip6h, IPV6HDRSIZE); ++ } ++ else { ++ ret = pskb_expand_head(old_skb, 0, IPV6V4HDRDELTA, GFP_ATOMIC); ++ if (unlikely(ret)) { ++ nat46debug(0, "[nat46] Could not copy v4 skb"); ++ *sport = *dport = 0; ++ return 0; ++ } ++ ++ skb_put(old_skb, IPV6V4HDRDELTA); ++ iiph = (struct iphdr *)(icmp_hdr(old_skb) + 1); ++ memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ntohs(iiph->tot_len) - IPV4HDRSIZE); ++ memcpy(iiph, &ip6h, IPV6HDRSIZE); ++ nat46 = get_nat46_instance(old_skb); ++ iph = ip_hdr(old_skb); ++ } ++ ++ /* Swapping Ports for outer header */ ++ /* Another work-around till LPM is not present. */ ++ temp_port = *sport; ++ *sport = *dport; ++ *dport = temp_port; ++ ++ return 1; ++} ++ + int nat46_ipv4_input(struct sk_buff *old_skb) { + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + struct sk_buff *new_skb; +- uint16_t sport = 0, dport = 0; ++ uint16_t sport = 0, dport = 0, ret = 0; + + int err = 0; + uint8_t tclass = 0; +@@ -1879,7 +2043,7 @@ int nat46_ipv4_input(struct sk_buff *old + } + nat46debug(1, "nat46_ipv4_input packet"); + nat46debug(5, "nat46_ipv4_input protocol: %d, len: %d, flags: %02x", hdr4->protocol, old_skb->len, IPCB(old_skb)->flags); +- if(0 == (ntohs(hdr4->frag_off) & 0x3FFF) ) { ++ if (0 == (ntohs(hdr4->frag_off) & 0x3FFF)) { + check_for_l4 = 1; + } else if (IPPROTO_ICMP == hdr4->protocol) { + /* +@@ -1916,9 +2080,10 @@ int nat46_ipv4_input(struct sk_buff *old + break; + } + case IPPROTO_ICMP: +- sport = dport = nat46_fixup_icmp(nat46, hdr4, old_skb); +- having_l4 = 1; +- break; ++ ret = nat46_fixup_icmp(nat46, hdr4, old_skb, &sport, &dport); ++ nat46debug(3, "ICMP translated to dest port %d, and src port %d.", ntohs(dport), ntohs(sport)); ++ having_l4 = 1; ++ break; + default: + break; + } diff --git a/package/kernel/nat46/patches/105-longest-prefix-match.patch b/package/kernel/nat46/patches/105-longest-prefix-match.patch new file mode 100644 index 00000000000000..95fe6af973f45b --- /dev/null +++ b/package/kernel/nat46/patches/105-longest-prefix-match.patch @@ -0,0 +1,640 @@ +Author: Pavithra R +Date: Tue Aug 4 10:33:59 2020 +0530 + +nat46: Adding support for multiple MAP-T rules. + +This patch is propagated from kernel 4.4 commit +05a122b0cb0d3a99f040c94b3f626e7350f1445b + +This change covers: +1. Support for adding maximum of 32 MAP-T rules (DMR + FMRs). +2. Support for rule lookup based on Longest Prefix Match method. +3. Support for validation of new rules being inserted. + +Change-Id: Id87448a8f544273b40c20aaab6e5c63b0dbd72e +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -128,6 +128,13 @@ int try_parse_ipv6_prefix(struct in6_add + *arg_plen++ = 0; + if (pref_len) { + *pref_len = simple_strtol(arg_plen, NULL, 10); ++ ++ /* ++ * ipv6 prefix should be <= 128 ++ */ ++ if (*pref_len > IPV6_BITS_MAX) { ++ return -1; ++ } + } + } + err = (1 != in6_pton(arg, -1, (u8 *)pref, '\0', NULL)); +@@ -141,6 +148,13 @@ int try_parse_ipv4_prefix(u32 *v4addr, i + *arg_plen++ = 0; + if (pref_len) { + *pref_len = simple_strtol(arg_plen, NULL, 10); ++ ++ /* ++ * ipv4 prefix len should be <= 32 ++ */ ++ if (*pref_len > IPV4_BITS_MAX) { ++ return -1; ++ } + } + } + err = (1 != in4_pton(arg, -1, (u8 *)v4addr, '/', NULL)); +@@ -183,11 +197,127 @@ int try_parse_rule_arg(nat46_xlate_rule_ + return err; + } + +-/* +- * Parse the config commands in the buffer, +- * destructive (puts zero between the args) ++static inline void nat46_swap(nat46_xlate_rulepair_t *var1, nat46_xlate_rulepair_t *var2) { ++ nat46_xlate_rulepair_t temp; ++ temp = *var1; ++ *var1 = *var2; ++ *var2 = temp; ++} ++ ++/* ++ * Sort rule pairs based on prefix length. + */ ++void nat46_sort_rule_array(nat46_instance_t *nat46) { ++ int i, j; ++ int nelem = nat46->npairs; ++ nat46_xlate_rulepair_t *array = NULL; ++ ++ memcpy(nat46->sorted_ipv4_local_pairs, nat46->pairs, nelem * sizeof(nat46_xlate_rulepair_t)); ++ memcpy(nat46->sorted_ipv4_remote_pairs, nat46->pairs, nelem * sizeof(nat46_xlate_rulepair_t)); ++ memcpy(nat46->sorted_ipv6_local_pairs, nat46->pairs, nelem * sizeof(nat46_xlate_rulepair_t)); ++ memcpy(nat46->sorted_ipv6_remote_pairs, nat46->pairs, nelem * sizeof(nat46_xlate_rulepair_t)); ++ ++ array = &nat46->sorted_ipv4_local_pairs[0]; ++ for (i = 0; i < nelem - 1; i++) { ++ for (j = 0; j < nelem - i - 1; j++) { ++ if (array[j].local.v4_pref_len < array[j+1].local.v4_pref_len) { ++ nat46_swap (&array[j], &array[j+1]); ++ } ++ } ++ } ++ ++ array = &nat46->sorted_ipv4_remote_pairs[0]; ++ for (i = 0; i < nelem - 1; i++) { ++ for (j = 0; j < nelem - i - 1; j++) { ++ if (array[j].remote.v4_pref_len < array[j+1].remote.v4_pref_len) { ++ nat46_swap (&array[j], &array[j+1]); ++ } ++ } ++ } + ++ array = &nat46->sorted_ipv6_local_pairs[0]; ++ for (i = 0; i < nelem - 1; i++) { ++ for (j = 0; j < nelem - i - 1; j++) { ++ if (array[j].local.v6_pref_len < array[j+1].local.v6_pref_len) { ++ nat46_swap (&array[j], &array[j+1]); ++ } ++ } ++ } ++ ++ array = &nat46->sorted_ipv6_remote_pairs[0]; ++ for (i = 0; i < nelem - 1; i++) { ++ for (j = 0; j < nelem - i - 1; j++) { ++ if (array[j].remote.v6_pref_len < array[j+1].remote.v6_pref_len) { ++ nat46_swap (&array[j], &array[j+1]); ++ } ++ } ++ } ++} ++ ++bool nat46_validate_RFC6052_style(nat46_instance_t *nat46, nat46_xlate_rule_t rule) ++{ ++ if (rule.style == NAT46_XLATE_RFC6052) { ++ if (!((rule.v6_pref_len == 32) || (rule.v6_pref_len == 40) || ++ (rule.v6_pref_len == 48) || (rule.v6_pref_len == 56) || ++ (rule.v6_pref_len == 64) || (rule.v6_pref_len == 96))) { ++ nat46debug(3, "IPv6 prefix len is invalid"); ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool nat46_validate_MAP_style(nat46_instance_t *nat46, nat46_xlate_rule_t rule) ++{ ++ int psid_len; ++ if (rule.style == NAT46_XLATE_MAP) { ++ ++ /* ++ * max ea_len is 48 ++ */ ++ if (rule.ea_len > EA_LEN_MAX) { ++ nat46debug(3, "EA-length should not exceed 48"); ++ return false; ++ } ++ ++ if (rule.v4_pref_len + rule.ea_len > IPV4_BITS_MAX) { ++ psid_len = rule.ea_len - (IPV4_BITS_MAX - rule.v4_pref_len); ++ } else { ++ psid_len = 0; ++ } ++ ++ if (psid_len + rule.psid_offset > PSID_LEN_MAX) { ++ nat46debug(3, "psid_len + psid_offset should not exceed 16"); ++ return false; ++ } ++ } ++ return true; ++} ++ ++int nat46_validate_ipair_config(nat46_instance_t *nat46, nat46_xlate_rulepair_t *apair) ++{ ++ if (!nat46_validate_RFC6052_style(nat46, apair->local)) { ++ return -1; ++ } ++ ++ if (!nat46_validate_RFC6052_style(nat46, apair->remote)) { ++ return -1; ++ } ++ ++ if (!nat46_validate_MAP_style(nat46, apair->local)) { ++ return -1; ++ } ++ ++ if (!nat46_validate_MAP_style(nat46, apair->remote)) { ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Parse the config commands in the buffer, ++ * destructive (puts zero between the args) ++ */ + int nat46_set_ipair_config(nat46_instance_t *nat46, int ipair, char *buf, int count) { + char *tail = buf; + char *arg_name; +@@ -217,7 +347,18 @@ int nat46_set_ipair_config(nat46_instanc + err = try_parse_rule_arg(&apair->remote, arg_name, &tail); + } + } +- return err; ++ ++ err = nat46_validate_ipair_config(nat46, apair); ++ if (err) { ++ return err; ++ } ++ ++ /* ++ * sort nat46->pairs based on prefix length. ++ */ ++ nat46_sort_rule_array(nat46); ++ ++ return 0; + } + + int nat46_set_config(nat46_instance_t *nat46, char *buf, int count) { +@@ -933,37 +1074,120 @@ int is_last_pair_in_group(nat46_xlate_ru + return ( (apair->local.style != NAT46_XLATE_NONE) && (apair->remote.style != NAT46_XLATE_NONE) ); + } + ++nat46_xlate_rulepair_t *nat46_lpm(nat46_instance_t *nat46, nat46_rule_type_t type, void *paddr) { ++ int ipair = 0; ++ nat46_xlate_rulepair_t *apair = NULL; ++ uint32_t mask = 0; ++ uint8_t *pa1; ++ uint8_t *pa2; ++ ++ if(!nat46 || !paddr) { ++ return NULL; ++ } ++ ++ switch (type) { ++ case NAT46_IPV4_LOCAL: ++ for (ipair = 0; ipair < nat46->npairs; ipair++) { ++ apair = &nat46->sorted_ipv4_local_pairs[ipair]; ++ ++ /* ++ * For a 32-bit number, if the shift count is 32, then the ++ * result of the left shift operation is always 0. ++ */ ++ if (apair->local.v4_pref_len) { ++ mask = htonl(U32_MASK << (IPV4_BITS_MAX - apair->local.v4_pref_len)); ++ } ++ ++ if((*(uint32_t *)paddr & mask) == (apair->local.v4_pref & mask)) { ++ return apair; ++ } ++ } ++ break; ++ case NAT46_IPV4_REMOTE: ++ for (ipair = 0; ipair < nat46->npairs; ipair++) { ++ apair = &nat46->sorted_ipv4_remote_pairs[ipair]; ++ ++ /* ++ * For a 32-bit number, if the shift count is 32, then the ++ * result of the left shift operation is always 0. ++ */ ++ if (apair->remote.v4_pref_len) { ++ mask = htonl(U32_MASK << (IPV4_BITS_MAX - apair->remote.v4_pref_len)); ++ } ++ ++ if((*(uint32_t *)paddr & mask) == (apair->remote.v4_pref & mask)) { ++ return apair; ++ } ++ } ++ break; ++ case NAT46_IPV6_LOCAL: ++ for (ipair = 0; ipair < nat46->npairs; ipair++) { ++ apair = &nat46->sorted_ipv6_local_pairs[ipair]; ++ if(memcmp(paddr, &apair->local.v6_pref, apair->local.v6_pref_len / BITS_PER_BYTE)) { ++ continue; ++ } ++ if(apair->local.v6_pref_len % BITS_PER_BYTE) { ++ mask = U8_MASK << (BITS_PER_BYTE - (apair->local.v6_pref_len % BITS_PER_BYTE)); ++ pa1 = (uint8_t *)paddr + (apair->local.v6_pref_len / BITS_PER_BYTE); ++ pa2 = (uint8_t *)&apair->local.v6_pref + (apair->local.v6_pref_len / BITS_PER_BYTE); ++ ++ if ((*pa1 & mask) == (*pa2 & mask)) { ++ return apair; ++ } ++ } ++ else ++ return apair; ++ } ++ break; ++ case NAT46_IPV6_REMOTE: ++ for (ipair = 0; ipair < nat46->npairs; ipair++) { ++ apair = &nat46->sorted_ipv6_remote_pairs[ipair]; ++ if(memcmp(paddr, &apair->remote.v6_pref, apair->remote.v6_pref_len / BITS_PER_BYTE)) { ++ continue; ++ } ++ if(apair->remote.v6_pref_len % BITS_PER_BYTE) { ++ mask = U8_MASK << (BITS_PER_BYTE - (apair->remote.v6_pref_len % BITS_PER_BYTE)); ++ pa1 = (uint8_t *)paddr + (apair->remote.v6_pref_len / BITS_PER_BYTE); ++ pa2 = (uint8_t *)&apair->remote.v6_pref + (apair->remote.v6_pref_len / BITS_PER_BYTE); ++ ++ if((*pa1 & mask) == (*pa2 & mask)) { ++ return apair; ++ } ++ } ++ else ++ return apair; ++ } ++ break; ++ default: ++ nat46debug(0, "%s : Invalid prefix type.\n", __func__); ++ } ++ return NULL; ++} ++ + void pairs_xlate_v6_to_v4_inner(nat46_instance_t *nat46, struct ipv6hdr *ip6h, __u32 *pv4saddr, __u32 *pv4daddr) { + int ipair = 0; + nat46_xlate_rulepair_t *apair = NULL; + int xlate_src = -1; + int xlate_dst = -1; + +- for(ipair = 0; ipair < nat46->npairs; ipair++) { +- apair = &nat46->pairs[ipair]; ++ apair = nat46_lpm(nat46, NAT46_IPV6_REMOTE, &ip6h->daddr); ++ if (!apair) { ++ return; ++ } + +- if(-1 == xlate_dst) { +- if(xlate_v6_to_v4(nat46, &apair->remote, &ip6h->daddr, pv4daddr)) { +- xlate_dst = ipair; +- } +- } +- if(-1 == xlate_src) { +- if(xlate_v6_to_v4(nat46, &apair->local, &ip6h->saddr, pv4saddr)) { +- xlate_src = ipair; +- } +- } +- if((xlate_src >= 0) && (xlate_dst >= 0)) { +- /* we did manage to translate it */ +- break; +- } else { +- /* We did not match fully and there are more rules */ +- if((ipair+1 < nat46->npairs) && is_last_pair_in_group(apair)) { +- xlate_src = -1; +- xlate_dst = -1; +- } +- } ++ if (xlate_v6_to_v4(nat46, &apair->remote, &ip6h->daddr, pv4daddr)) { ++ xlate_dst = ipair; ++ } ++ if (xlate_v6_to_v4(nat46, &apair->local, &ip6h->saddr, pv4saddr)) { ++ xlate_src = ipair; ++ } ++ ++ if ((xlate_src >= 0) && (xlate_dst >= 0)) { ++ /* we did manage to translate it */ ++ nat46debug(5, "[nat46payload] xlate results: src %d dst %d", xlate_src, xlate_dst); ++ } else { ++ nat46debug(1, "[nat46] Could not find a translation pair v6->v4 src %pI6c dst %pI6c", &ip6h->saddr, &ip6h->daddr); + } +- nat46debug(5, "[nat46payload] xlate results: src %d dst %d", xlate_src, xlate_dst); + } + + /* +@@ -1557,40 +1781,31 @@ static uint16_t nat46_fixup_icmp(nat46_i + return ret; + } + +-int pairs_xlate_v6_to_v4_outer(nat46_instance_t *nat46, struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { ++int pairs_xlate_v6_to_v4_outer(nat46_instance_t *nat46, nat46_xlate_rulepair_t *apair, ++ struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { + int ipair = 0; +- nat46_xlate_rulepair_t *apair = NULL; + int xlate_src = -1; + int xlate_dst = -1; + + for(ipair = 0; ipair < nat46->npairs; ipair++) { +- apair = &nat46->pairs[ipair]; +- +- if(-1 == xlate_dst) { +- if (xlate_v6_to_v4(nat46, &apair->local, &ip6h->daddr, pv4daddr)) { +- nat46debug(5, "Dst addr %pI6 to %pI4 \n", &ip6h->daddr, pv4daddr); +- xlate_dst = ipair; +- } ++ apair = nat46_lpm(nat46, NAT46_IPV6_REMOTE, &ip6h->saddr); ++ if (!apair) { ++ return 0; + } +- if(-1 == xlate_src) { +- if (xlate_v6_to_v4(nat46, &apair->remote, &ip6h->saddr, pv4saddr)) { +- nat46debug(5, "Src addr %pI6 to %pI4 \n", &ip6h->saddr, pv4saddr); +- xlate_src = ipair; +- } ++ ++ if (xlate_v6_to_v4(nat46, &apair->local, &ip6h->daddr, pv4daddr)) { ++ nat46debug(5, "Dst addr %pI6 to %pI4 \n", &ip6h->daddr, pv4daddr); ++ xlate_dst = ipair; + } +- if( (xlate_src >= 0) && (xlate_dst >= 0) ) { +- break; +- } else { +- /* We did not match fully and there are more rules */ +- if((ipair+1 < nat46->npairs) && is_last_pair_in_group(apair)) { +- xlate_src = -1; +- xlate_dst = -1; +- } ++ ++ if (xlate_v6_to_v4(nat46, &apair->remote, &ip6h->saddr, pv4saddr)) { ++ nat46debug(5, "Src addr %pI6 to %pI4 \n", &ip6h->saddr, pv4saddr); ++ xlate_src = ipair; + } + } + if (xlate_dst >= 0) { + if (xlate_src < 0) { +- if(proto == NEXTHDR_ICMP) { ++ if (proto == NEXTHDR_ICMP) { + nat46debug(1, "[nat46] Could not translate remote address v6->v4, ipair %d, for ICMP6 use dest addr", ipair); + *pv4saddr = *pv4daddr; + xlate_src = xlate_dst; +@@ -1606,12 +1821,14 @@ int pairs_xlate_v6_to_v4_outer(nat46_ins + } + + int xlate_6_to_4(struct net_device *dev, struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { +- return pairs_xlate_v6_to_v4_outer(netdev_nat46_instance(dev), ip6h, proto, pv4saddr, pv4daddr); ++ nat46_xlate_rulepair_t apair; ++ return pairs_xlate_v6_to_v4_outer(netdev_nat46_instance(dev), &apair, ip6h, proto, pv4saddr, pv4daddr); + } + EXPORT_SYMBOL(xlate_6_to_4); + + int nat46_ipv6_input(struct sk_buff *old_skb) { + struct ipv6hdr *ip6h = ipv6_hdr(old_skb); ++ nat46_xlate_rulepair_t apair; + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + uint16_t proto; + uint16_t frag_off; +@@ -1684,7 +1901,7 @@ int nat46_ipv6_input(struct sk_buff *old + check_for_l4 = 1; + } + +- if(!pairs_xlate_v6_to_v4_outer(nat46, ip6h, proto, &v4saddr, &v4daddr)) { ++ if (!pairs_xlate_v6_to_v4_outer(nat46, &apair, ip6h, proto, &v4saddr, &v4daddr)) { + nat46debug(0, "[nat46] Could not translate v6->v4"); + goto done; + } +@@ -1837,56 +2054,44 @@ int ip4_input_not_interested(nat46_insta + return 0; + } + +-int pairs_xlate_v4_to_v6_outer(nat46_instance_t *nat46, struct iphdr *hdr4, uint16_t *sport, uint16_t *dport, void *v6saddr, void *v6daddr) { ++int pairs_xlate_v4_to_v6_outer(nat46_instance_t *nat46, nat46_xlate_rulepair_t *apair, ++ struct iphdr *hdr4, uint16_t *sport, uint16_t *dport, void *v6saddr, void *v6daddr) { + int ipair = 0; +- nat46_xlate_rulepair_t *apair = NULL; + int xlate_src = -1; + int xlate_dst = -1; ++ int ret = 0; + +- for(ipair = 0; ipair < nat46->npairs; ipair++) { +- apair = &nat46->pairs[ipair]; ++ apair = nat46_lpm(nat46, NAT46_IPV4_REMOTE, &hdr4->daddr); ++ if (!apair) { ++ return 0; ++ } + +- if(-1 == xlate_src) { +- if(xlate_v4_to_v6(nat46, &apair->local, &hdr4->saddr, v6saddr, sport)) { +- nat46debug(5, "Src addr %pI4 to %pI6 \n", &hdr4->saddr, v6saddr); +- xlate_src = ipair; +- } +- } +- if(-1 == xlate_dst) { +- if(xlate_v4_to_v6(nat46, &apair->remote, &hdr4->daddr, v6daddr, dport)) { +- nat46debug(5, "Dst addr %pI4 to %pI6 \n", &hdr4->daddr, v6daddr); +- xlate_dst = ipair; +- } +- } +- if( (xlate_src >= 0) && (xlate_dst >= 0) ) { +- break; +- } else { +- /* We did not match fully and there are more rules */ +- if((ipair+1 < nat46->npairs) && is_last_pair_in_group(apair)) { +- xlate_src = -1; +- xlate_dst = -1; +- } +- } ++ if (xlate_v4_to_v6(nat46, &apair->local, &hdr4->saddr, v6saddr, sport)) { ++ nat46debug(5, "Src addr %pI4 to %pI6 \n", &hdr4->saddr, v6saddr); ++ xlate_src = ipair; ++ } ++ if (xlate_v4_to_v6(nat46, &apair->remote, &hdr4->daddr, v6daddr, dport)) { ++ nat46debug(5, "Dst addr %pI4 to %pI6 \n", &hdr4->daddr, v6daddr); ++ xlate_dst = ipair; + } + nat46debug(5, "[nat46] pairs_xlate_v4_to_v6_outer result: src %d dst %d", xlate_src, xlate_dst); + if ( (xlate_src >= 0) && (xlate_dst >= 0) ) { +- return 1; ++ ret = 1; ++ } else { ++ nat46debug(1, "[nat46] Could not find a translation pair v4->v6"); + } +- +- nat46debug(1, "[nat46] Could not find a translation pair v4->v6"); +- +- return 0; ++ return ret; + } + + int xlate_4_to_6(struct net_device *dev, struct iphdr *hdr4, uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr) { +- return pairs_xlate_v4_to_v6_outer(netdev_nat46_instance(dev), hdr4, &sport, &dport, v6saddr, v6daddr); ++ nat46_xlate_rulepair_t apair; ++ return pairs_xlate_v4_to_v6_outer(netdev_nat46_instance(dev), &apair, hdr4, &sport, &dport, v6saddr, v6daddr); + } + EXPORT_SYMBOL(xlate_4_to_6); + +-/* FIXME: This is a workaround, till the LPM is not added. The sport & dport in inner header will be dport & sport of the outer +- * header, respectively. Hence, dest. and source ips of inner header will be found in local & remote rules, respectively. +- * Will work only for a pair of local & remote rules. Once LPM is brought in, this method can be removed and +- * pairs_xlate_v4_to_v6_outer be used instead. ++/* ++ * The sport & dport in inner header will be dport & sport of the outer header, respectively. ++ * Hence, dest. and source ips of inner header will be found in local & remote rules, respectively. + */ + int pairs_xlate_v4_to_v6_inner(nat46_instance_t *nat46, struct iphdr *iph, + uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr) { +@@ -1895,35 +2100,27 @@ int pairs_xlate_v4_to_v6_inner(nat46_ins + int xlate_src = -1; + int xlate_dst = -1; + +- for (ipair = 0; ipair < nat46->npairs; ipair++) { +- apair = &nat46->pairs[ipair]; ++ apair = nat46_lpm(nat46, NAT46_IPV4_REMOTE, &iph->saddr); ++ if (!apair) { ++ return 0; ++ } + +- if (-1 == xlate_dst) { +- if (xlate_v4_to_v6(nat46, &apair->local, &iph->daddr, v6daddr, &dport)) { +- nat46debug(3, "Dst addr %pI4 to %pI6 \n", &iph->daddr, v6daddr); +- xlate_dst = ipair; +- } +- } +- if (-1 == xlate_src) { +- if(xlate_v4_to_v6(nat46, &apair->remote, &iph->saddr, v6saddr, &sport)) { +- nat46debug(3, "Src addr %pI4 to %pI6 \n", &iph->saddr, v6saddr); +- xlate_src = ipair; +- } +- } +- if ((xlate_src >= 0) && (xlate_dst >= 0)) { +- /* we did manage to translate it */ +- nat46debug(5, "[nat46] Inner header xlate results: src %d dst %d", xlate_src, xlate_dst); +- return 1; +- } else { +- /* We did not match fully and there are more rules */ +- if((ipair+1 < nat46->npairs) && is_last_pair_in_group(apair)) { +- xlate_src = -1; +- xlate_dst = -1; +- } +- } +-} ++ if (xlate_v4_to_v6(nat46, &apair->local, &iph->daddr, v6daddr, &dport)) { ++ nat46debug(3, "Dst addr %pI4 to %pI6 \n", &iph->daddr, v6daddr); ++ xlate_dst = ipair; ++ } ++ if (xlate_v4_to_v6(nat46, &apair->remote, &iph->saddr, v6saddr, &sport)) { ++ nat46debug(3, "Src addr %pI4 to %pI6 \n", &iph->saddr, v6saddr); ++ xlate_src = ipair; ++ } ++ if ((xlate_src >= 0) && (xlate_dst >= 0)) { ++ /* we did manage to translate it */ ++ nat46debug(5, "[nat46] Inner header xlate results: src %d dst %d", xlate_src, xlate_dst); ++ return 1; ++ } else { ++ nat46debug(1, "[nat46] Could not find a translation pair v4->v6"); ++ } + +- nat46debug(1, "[nat46] Could not find a translation pair v4->v6"); + return 0; + } + +@@ -2020,6 +2217,7 @@ static uint16_t xlate_pkt_in_err_v4_to_v + + int nat46_ipv4_input(struct sk_buff *old_skb) { + nat46_instance_t *nat46 = get_nat46_instance(old_skb); ++ nat46_xlate_rulepair_t apair; + struct sk_buff *new_skb; + uint16_t sport = 0, dport = 0, ret = 0; + +@@ -2097,7 +2295,7 @@ int nat46_ipv4_input(struct sk_buff *old + having_l4 = 1; + } + +- if(!pairs_xlate_v4_to_v6_outer(nat46, hdr4, having_l4 ? &sport : NULL, having_l4 ? &dport : NULL, v6saddr, v6daddr)) { ++ if(!pairs_xlate_v4_to_v6_outer(nat46, &apair, hdr4, having_l4 ? &sport : NULL, having_l4 ? &dport : NULL, v6saddr, v6daddr)) { + nat46debug(0, "[nat46] Could not translate v4->v6"); + goto done; + } +--- a/nat46/modules/nat46-core.h ++++ b/nat46/modules/nat46-core.h +@@ -23,6 +23,15 @@ + // #define nat46debug(level, format, ...) + #define nat46debug(level, format, ...) do { if(nat46->debug >= level) { printk(format "\n", ##__VA_ARGS__); } } while (0) + ++#define U8_MASK (uint8_t)(0xFF) ++#define U32_MASK (uint32_t)(~0U) ++#define BITS_PER_BYTE 8 ++#define PSID_LEN_MAX 16 ++#define NUM_RULE_PAIRS_MAX 32 ++#define IPV4_BITS_MAX 32 ++#define EA_LEN_MAX 48 ++#define IPV6_BITS_MAX 128 ++ + #define IPV6HDRSIZE 40 + #define IPV4HDRSIZE 20 + #define IPV6V4HDRDELTA (IPV6HDRSIZE - IPV4HDRSIZE) +@@ -39,6 +48,17 @@ typedef enum { + NAT46_XLATE_RFC6052 + } nat46_xlate_style_t; + ++/* ++ * Enumeration for sorting pairs based on ++ * type of prefix length. ++ */ ++typedef enum { ++ NAT46_IPV4_LOCAL = 0, ++ NAT46_IPV4_REMOTE, ++ NAT46_IPV6_LOCAL, ++ NAT46_IPV6_REMOTE ++} nat46_rule_type_t; ++ + #define NAT46_SIGNATURE 0x544e3634 + #define FREED_NAT46_SIGNATURE 0xdead544e + +@@ -64,7 +84,11 @@ typedef struct { + int debug; + + int npairs; +- nat46_xlate_rulepair_t pairs[0]; /* npairs */ ++ nat46_xlate_rulepair_t pairs[NUM_RULE_PAIRS_MAX]; /* npairs */ ++ nat46_xlate_rulepair_t sorted_ipv4_local_pairs[NUM_RULE_PAIRS_MAX]; /* npairs */ ++ nat46_xlate_rulepair_t sorted_ipv4_remote_pairs[NUM_RULE_PAIRS_MAX]; /* npairs */ ++ nat46_xlate_rulepair_t sorted_ipv6_local_pairs[NUM_RULE_PAIRS_MAX]; /* npairs */ ++ nat46_xlate_rulepair_t sorted_ipv6_remote_pairs[NUM_RULE_PAIRS_MAX]; /* npairs */ + } nat46_instance_t; + + int nat46_ipv6_input(struct sk_buff *old_skb); +--- a/nat46/modules/nat46-netdev.c ++++ b/nat46/modules/nat46-netdev.c +@@ -270,7 +270,14 @@ int nat46_insert(char *devname, char *bu + int ret = -1; + if(dev) { + nat46_instance_t *nat46 = netdev_nat46_instance(dev); +- nat46_instance_t *nat46_new = alloc_nat46_instance(nat46->npairs+1, nat46, 0, 1, -1); ++ nat46_instance_t *nat46_new; ++ ++ if(nat46->npairs == NUM_RULE_PAIRS_MAX) { ++ printk("Could not insert a new rule on device %s\n", devname); ++ return ret; ++ } ++ ++ nat46_new = alloc_nat46_instance(nat46->npairs+1, nat46, 0, 1, -1); + if(nat46_new) { + netdev_nat46_set_instance(dev, nat46_new); + ret = nat46_set_ipair_config(nat46_new, 0, buf, strlen(buf)); diff --git a/package/kernel/nat46/patches/106-dummy_header.patch b/package/kernel/nat46/patches/106-dummy_header.patch new file mode 100644 index 00000000000000..1d4351166fb963 --- /dev/null +++ b/package/kernel/nat46/patches/106-dummy_header.patch @@ -0,0 +1,104 @@ +Author: Pavithra R +Date: Wed Aug 5 10:09:45 2020 +0530 + +nat46: Add dummy fragment header for DF=0 IPv4 packet. + +This patch is propagated from 4.4 kernel commit +b45f19e86ebcc19ea26d5e014bfdcb837148f99e. + +Add dummy fragment header to IPv6 translated packet for +every DF=0 IPv4 packet. + +Change-Id: Id72945eefac030e95e4fd18305e48c46e525def3 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1996,6 +1996,27 @@ done: + + + ++/* ++ * Function to get MAP-T rules and flags. ++ */ ++bool nat46_get_info(struct net_device *dev, nat46_xlate_rulepair_t **nat46_rule_pair, ++ int *count, u8 *flag) { ++ if ((!dev) || (!nat46_rule_pair) || (!count) || (!flag)) { ++ return false; ++ } ++ ++ if (!nat46_get_rule_config(dev, nat46_rule_pair, count)) { ++ return false; ++ } ++ ++ /* Check add dummy header flag */ ++ if (add_dummy_header) { ++ *flag = ADD_DUMMY_HEADER; ++ } ++ return true; ++} ++EXPORT_SYMBOL(nat46_get_info); ++ + void ip6_update_csum(struct sk_buff * skb, struct ipv6hdr * ip6hdr, int do_atomic_frag) + { + u32 sum1=0; +@@ -2254,6 +2275,11 @@ int nat46_ipv4_input(struct sk_buff *old + } + hdr4 = ip_hdr(old_skb); + check_for_l4 = 1; ++ if (add_dummy_header) { ++ if (0 == (ntohs(hdr4->frag_off) & IP_DF)) { ++ add_frag_header = 1; ++ } ++ } + } else { + add_frag_header = 1; + if (0 == (ntohs(hdr4->frag_off) & 0x1FFF)) { +--- a/nat46/modules/nat46-core.h ++++ b/nat46/modules/nat46-core.h +@@ -32,6 +32,9 @@ + #define EA_LEN_MAX 48 + #define IPV6_BITS_MAX 128 + ++/* Flag definations for MAP-T */ ++#define ADD_DUMMY_HEADER 0x01 ++ + #define IPV6HDRSIZE 40 + #define IPV4HDRSIZE 20 + #define IPV6V4HDRDELTA (IPV6HDRSIZE - IPV4HDRSIZE) +@@ -110,5 +113,6 @@ int xlate_6_to_4(struct net_device *dev, + int xlate_4_to_6(struct net_device *dev, struct iphdr *hdr4, uint16_t sport, uint16_t dport, void *v6saddr, void *v6daddr); + bool nat46_get_rule_config(struct net_device *dev, nat46_xlate_rulepair_t **nat46_rule_pair, int *count); + int nat46_get_npairs(struct net_device *dev); +- ++bool nat46_get_info(struct net_device *dev, nat46_xlate_rulepair_t **nat46_rule_pair, ++ int *count, u8 *flag); + #endif +--- a/nat46/modules/nat46-module.c ++++ b/nat46/modules/nat46-module.c +@@ -56,12 +56,16 @@ MODULE_AUTHOR("Andrew Yourtchenko +Date: Wed Aug 5 10:57:25 2020 +0530 + +nat46: Add support for 64-bits stats. + +This patch is propagated from 4.4 kernel commit +4a2d1dd9bc9331392c7a4947126c361217c82e0c + +Add 64-bits stats functionality for MAP-T interface. + +Change-Id: I4a6f9c7ed3554ac0ec672aa5fa283be2e95cfdc0 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-netdev.c ++++ b/nat46/modules/nat46-netdev.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include "nat46-core.h" + #include "nat46-module.h" +@@ -40,16 +41,40 @@ static u8 netdev_count = 0; + + static int nat46_netdev_up(struct net_device *dev); + static int nat46_netdev_down(struct net_device *dev); +- ++static int nat46_netdev_init(struct net_device *dev); ++static void nat46_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *tot); + static netdev_tx_t nat46_netdev_xmit(struct sk_buff *skb, struct net_device *dev); + + + static const struct net_device_ops nat46_netdev_ops = { ++ .ndo_init = nat46_netdev_init, /* device specific initialization */ + .ndo_open = nat46_netdev_up, /* Called at ifconfig nat46 up */ + .ndo_stop = nat46_netdev_down, /* Called at ifconfig nat46 down */ + .ndo_start_xmit = nat46_netdev_xmit, /* REQUIRED, must return NETDEV_TX_OK */ ++ .ndo_get_stats64 = nat46_get_stats64, /* 64 bit device stats */ + }; + ++static int nat46_netdev_init(struct net_device *dev) ++{ ++ int i; ++ dev->tstats = alloc_percpu(struct pcpu_sw_netstats); ++ if (!dev->tstats) { ++ return -ENOMEM; ++ } ++ ++ for_each_possible_cpu(i) { ++ struct pcpu_sw_netstats *ipt_stats; ++ ipt_stats = per_cpu_ptr(dev->tstats, i); ++ u64_stats_init(&ipt_stats->syncp); ++ } ++ return 0; ++} ++ ++static void nat46_netdev_resource_free(struct net_device *dev) ++{ ++ free_percpu(dev->tstats); ++} ++ + static int nat46_netdev_up(struct net_device *dev) + { + netif_start_queue(dev); +@@ -65,9 +90,14 @@ static int nat46_netdev_down(struct net_ + static netdev_tx_t nat46_netdev_xmit(struct sk_buff *skb, struct net_device *dev) + { + int ret = 0; ++ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_inc(&tstats->rx_packets); ++ u64_stats_add(&tstats->rx_bytes, skb->len); ++ u64_stats_update_end(&tstats->syncp); ++ put_cpu_ptr(tstats); + +- dev->stats.rx_packets++; +- dev->stats.rx_bytes += skb->len; + if(ETH_P_IP == ntohs(skb->protocol)) { + ret = nat46_ipv4_input(skb); + } +@@ -81,22 +111,39 @@ static netdev_tx_t nat46_netdev_xmit(str + } + + void nat46_netdev_count_xmit(struct sk_buff *skb, struct net_device *dev) { +- dev->stats.tx_packets++; +- dev->stats.tx_bytes += skb->len; ++ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_inc(&tstats->tx_packets); ++ u64_stats_add(&tstats->tx_bytes, skb->len); ++ u64_stats_update_end(&tstats->syncp); ++ put_cpu_ptr(tstats); + } + + void nat46_update_stats(struct net_device *dev, uint32_t rx_packets, uint32_t rx_bytes, + uint32_t tx_packets, uint32_t tx_bytes, uint32_t rx_dropped, uint32_t tx_dropped) + { +- dev->stats.rx_packets += rx_packets; +- dev->stats.rx_bytes += rx_bytes; +- dev->stats.tx_packets += tx_packets; +- dev->stats.tx_bytes += tx_bytes; ++ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->rx_packets, rx_packets); ++ u64_stats_add(&tstats->rx_bytes, rx_bytes); ++ u64_stats_add(&tstats->tx_packets, tx_packets); ++ u64_stats_add(&tstats->tx_bytes, tx_bytes); + dev->stats.rx_dropped += rx_dropped; + dev->stats.tx_dropped += tx_dropped; ++ u64_stats_update_end(&tstats->syncp); ++ put_cpu_ptr(tstats); + } + EXPORT_SYMBOL(nat46_update_stats); + ++static void nat46_get_stats64(struct net_device *dev, ++ struct rtnl_link_stats64 *tot) ++{ ++ netdev_stats_to_stats64(tot, &dev->stats); ++ dev_fetch_sw_netstats(tot, dev->tstats); ++} ++ + void *netdev_nat46_instance(struct net_device *dev) { + nat46_netdev_priv_t *priv = netdev_priv(dev); + return priv->nat46; +@@ -120,6 +167,7 @@ static void nat46_netdev_setup(struct ne + priv->nat46 = nat46; + + dev->netdev_ops = &nat46_netdev_ops; ++ dev->priv_destructor = nat46_netdev_resource_free; + dev->type = ARPHRD_NONE; + dev->hard_header_len = 0; + dev->addr_len = 0; diff --git a/package/kernel/nat46/patches/108-ce_port.patch b/package/kernel/nat46/patches/108-ce_port.patch new file mode 100644 index 00000000000000..ab6ab37b986e07 --- /dev/null +++ b/package/kernel/nat46/patches/108-ce_port.patch @@ -0,0 +1,134 @@ +Author: Pavithra R +Date: Wed Aug 5 18:59:20 2020 +0530 + +nat46: Copy CE's port number to IPv6 fragment header. + +This patch is propagated from kernel 4.4 commit +7886fd3eb081c7767b02685593bc1d19deaecba8 + +Copy CE's port number to the lower 16-bits of IPv6 identification +number. + +Change-Id: I6946e93bf8bed4c1378d19e75db0729097e0d9eb +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -31,6 +31,7 @@ + + static uint16_t xlate_pkt_in_err_v4_to_v6(nat46_instance_t *nat46, struct iphdr *iph, + struct sk_buff *old_skb, uint16_t *sport, uint16_t *dport); ++static DEFINE_SPINLOCK(port_id_lock); + + void + nat46debug_dump(nat46_instance_t *nat46, int level, void *addr, int len) +@@ -2236,6 +2237,73 @@ static uint16_t xlate_pkt_in_err_v4_to_v + return 1; + } + ++/* Return the port number from CE's port set */ ++static uint16_t nat46_get_ce_port(nat46_xlate_rulepair_t *pair, uint16_t sport) ++{ ++ /* ++ * 'psid_bits_len' represents number of bits in PSID. ++ * 'offset' represents offset of PSID in a port number. ++ */ ++ uint8_t psid_bits_len, offset, port_set_bitmask; ++ ++ /* ++ * 'psid16' represent PSID value. ++ * 'm' represents number of bits in excluded port set. ++ * 'a' represents number of bits in a 16-bit port number after PSID. ++ * It is used to control number of port in one contiguous port set. ++ * ++ * Name of a variable 'a' and 'm' is as per Appendix B of [RFC7597]. ++ */ ++ uint16_t psid16, value, m, a; ++ nat46_xlate_rule_t *rule; ++ ++ /* stores to last port number from CE's port set */ ++ static uint16_t port_num; ++ ++ rule = &pair->local; ++ offset = rule->psid_offset; ++ ++ if (rule->ea_len + rule->v4_pref_len > IPV4_BITS_MAX) { ++ psid_bits_len = rule->ea_len - (IPV4_BITS_MAX - rule->v4_pref_len); ++ } else { ++ return 0; ++ } ++ a = PSID_LEN_MAX - offset - psid_bits_len; ++ psid16 = (ntohs(sport) >> a) & (0xffff >> (PSID_LEN_MAX - psid_bits_len)); ++ ++ spin_lock(&port_id_lock); ++ ++ /* Start case */ ++ if (0 == port_num) { ++ m = (offset) ? 1 : 0; ++ port_num = (m << (PSID_LEN_MAX - offset)) | (psid16 << a); ++ value = port_num; ++ spin_unlock(&port_id_lock); ++ return value; ++ } ++ ++ /* End of one port set */ ++ port_set_bitmask = (1 << a) - 1; ++ value = port_num & port_set_bitmask; ++ if (0 == (value ^ port_set_bitmask)) { ++ m = port_num >> (PSID_LEN_MAX - offset); ++ m++; ++ /* End case */ ++ if (m >= (1 << offset)) { ++ m = (offset) ? 1 : 0; ++ } ++ port_num = (m << (PSID_LEN_MAX - offset)) | (psid16 << a); ++ value = port_num; ++ spin_unlock(&port_id_lock); ++ return value; ++ } ++ ++ port_num++; ++ value = port_num; ++ spin_unlock(&port_id_lock); ++ return value; ++} ++ + int nat46_ipv4_input(struct sk_buff *old_skb) { + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + nat46_xlate_rulepair_t apair; +@@ -2368,9 +2436,34 @@ int nat46_ipv4_input(struct sk_buff *old + + if (add_frag_header) { + struct frag_hdr *fh = (struct frag_hdr*)(hdr6 + 1); ++ uint16_t ce_port_num = 0; ++ ++ /* Flag to represent whether PSID is assigned to MAP-T node or not */ ++ bool is_psid = false; ++ + fh->frag_off = htons(((ntohs(hdr4->frag_off) >> 13) & 7) + ((ntohs(hdr4->frag_off) & 0x1FFF) << 3)); + fh->nexthdr = hdr4->protocol; +- fh->identification = htonl(ntohs(hdr4->id)); ++ ++ /* ++ * PSID assigned MAP-T node will have non-zero ea_len and we are currently ++ * only supporting NAT46_XLATE_MAP as the CE's rule style. ++ */ ++ is_psid = (apair.local.style == NAT46_XLATE_MAP) && apair.local.ea_len; ++ if (is_psid) { ++ ce_port_num = nat46_get_ce_port(nat46->pairs, sport); ++ nat46debug(10, "\n ce port number is %02x\n", ce_port_num); ++ ++ /* Assign CE's port number as the fragment identifier */ ++ if (ce_port_num) { ++ fh->identification = htonl(ce_port_num); ++ } else { ++ fh->identification = htonl(ntohs(hdr4->id)); ++ } ++ } else { ++ fh->identification = htonl(ntohs(hdr4->id)); ++ } ++ ++ + } + ip6_update_csum(new_skb, hdr6, add_frag_header); + diff --git a/package/kernel/nat46/patches/109-fragment_if_not_df_and_larger_than_mtu.patch b/package/kernel/nat46/patches/109-fragment_if_not_df_and_larger_than_mtu.patch new file mode 100644 index 00000000000000..333228a97496c3 --- /dev/null +++ b/package/kernel/nat46/patches/109-fragment_if_not_df_and_larger_than_mtu.patch @@ -0,0 +1,30 @@ +Author: Pavithra R +Date: Wed Aug 5 19:26:48 2020 +0530 + +nat46: Fix the issue of packets not fragmented + +This patch is propagated from the kernel 4.4 commit +e598f9c249092abd7c7978fe99b6690884f225c9 + +when packets size is larger than the MTU of dst, if DF flag is not set, +fragment it instead of dropping it with PktTooBig ICMPv6 message. + +Change-Id: I380d42f59bb4f46a45e542f251f5710f2cca8b62 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -2343,10 +2343,11 @@ int nat46_ipv4_input(struct sk_buff *old + } + hdr4 = ip_hdr(old_skb); + check_for_l4 = 1; +- if (add_dummy_header) { +- if (0 == (ntohs(hdr4->frag_off) & IP_DF)) { ++ if (0 == (ntohs(hdr4->frag_off) & IP_DF)) { ++ if (add_dummy_header) { + add_frag_header = 1; + } ++ old_skb->ignore_df = 1; + } + } else { + add_frag_header = 1; diff --git a/package/kernel/nat46/patches/110-icmp_error_not_handled.patch b/package/kernel/nat46/patches/110-icmp_error_not_handled.patch new file mode 100644 index 00000000000000..5697c609732ccf --- /dev/null +++ b/package/kernel/nat46/patches/110-icmp_error_not_handled.patch @@ -0,0 +1,100 @@ +Author: Pavithra R +Date: Wed Aug 5 20:16:27 2020 +0530 + +nat46: fix ICMPv6 error message dropped locally + +This patch is propagated from the kernel 4.4 commit +1b96bd0e9ee9182566b119741854c03bf4b94a99 + +While routing IPv6 packets from a customer-side translated device (CLAT) +to a provider-side translated device (PLAT), it is possible that the IPv6 +destination is unknown. In such a scenario, the IPv6 stack must send back +an ICMP error. However, the source IPv6 address of this error message does +not have a MAP-T translation. According to RFC2473, the translation layer +should use the tunnel's own IPv4 address for the IPv6 ICMP packet's source +address. + +Change-Id: I784473cddf9214843c466d10763cb66852139ef6 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1782,11 +1782,12 @@ static uint16_t nat46_fixup_icmp(nat46_i + return ret; + } + +-int pairs_xlate_v6_to_v4_outer(nat46_instance_t *nat46, nat46_xlate_rulepair_t *apair, ++int pairs_xlate_v6_to_v4_outer(nat46_instance_t *nat46, nat46_xlate_rulepair_t **papair, + struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { + int ipair = 0; + int xlate_src = -1; + int xlate_dst = -1; ++ nat46_xlate_rulepair_t *apair; + + for(ipair = 0; ipair < nat46->npairs; ipair++) { + apair = nat46_lpm(nat46, NAT46_IPV6_REMOTE, &ip6h->saddr); +@@ -1794,6 +1795,7 @@ int pairs_xlate_v6_to_v4_outer(nat46_ins + return 0; + } + ++ *papair = apair; + if (xlate_v6_to_v4(nat46, &apair->local, &ip6h->daddr, pv4daddr)) { + nat46debug(5, "Dst addr %pI6 to %pI4 \n", &ip6h->daddr, pv4daddr); + xlate_dst = ipair; +@@ -1822,14 +1824,14 @@ int pairs_xlate_v6_to_v4_outer(nat46_ins + } + + int xlate_6_to_4(struct net_device *dev, struct ipv6hdr *ip6h, uint16_t proto, __u32 *pv4saddr, __u32 *pv4daddr) { +- nat46_xlate_rulepair_t apair; ++ nat46_xlate_rulepair_t *apair; + return pairs_xlate_v6_to_v4_outer(netdev_nat46_instance(dev), &apair, ip6h, proto, pv4saddr, pv4daddr); + } + EXPORT_SYMBOL(xlate_6_to_4); + + int nat46_ipv6_input(struct sk_buff *old_skb) { + struct ipv6hdr *ip6h = ipv6_hdr(old_skb); +- nat46_xlate_rulepair_t apair; ++ nat46_xlate_rulepair_t *apair; + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + uint16_t proto; + uint16_t frag_off; +@@ -1903,8 +1905,37 @@ int nat46_ipv6_input(struct sk_buff *old + } + + if (!pairs_xlate_v6_to_v4_outer(nat46, &apair, ip6h, proto, &v4saddr, &v4daddr)) { +- nat46debug(0, "[nat46] Could not translate v6->v4"); +- goto done; ++ if (proto == NEXTHDR_ICMP) { ++ struct icmp6hdr *icmp6h = add_offset(ip6h, v6packet_l3size); ++ struct ipv6hdr *ip6h_inner = (struct ipv6hdr *) (icmp6h + 1); ++ struct ipv6hdr hdr6; ++ switch(icmp6h->icmp6_type) { ++ case ICMPV6_DEST_UNREACH: ++ case ICMPV6_PKT_TOOBIG: ++ case ICMPV6_TIME_EXCEED: ++ case ICMPV6_PARAMPROB: ++ /* ++ * For icmpv6 error message, using the original message ++ * address to locate the apair one more time according ++ * to the RFC 2473, and use the ipv4 address of the ++ * tunnel as SRC ipv4 address ++ */ ++ memcpy(&hdr6.saddr, &ip6h_inner->daddr, 16); ++ memcpy(&hdr6.daddr, &ip6h_inner->saddr, 16); ++ if (!pairs_xlate_v6_to_v4_outer(nat46, &apair, &hdr6, proto, &v4saddr, &v4daddr)) { ++ nat46debug(0, "[nat46] Could not translate v6->v4"); ++ goto done; ++ } ++ v4saddr = apair->local.v4_pref; ++ break; ++ default: ++ nat46debug(0, "[nat46] Could not translate v6->v4"); ++ goto done; ++ } ++ } else { ++ nat46debug(0, "[nat46] Could not translate v6->v4"); ++ goto done; ++ } + } + + if (check_for_l4) { diff --git a/package/kernel/nat46/patches/111-fix_null_point_reference.patch b/package/kernel/nat46/patches/111-fix_null_point_reference.patch new file mode 100644 index 00000000000000..4cef9db199d639 --- /dev/null +++ b/package/kernel/nat46/patches/111-fix_null_point_reference.patch @@ -0,0 +1,40 @@ +Author: Pavithra R +Date: Wed Aug 5 20:35:00 2020 +0530 + +nat46: Fix null pointer dereference issue + +This patch is propagated from the kernel 4.4 commit +5bdf9bd5500c45ab5a3fd43e60c40a09d5e5a13d + +get_nat46_instance possibly returns null point, before using the returning +point, caller needs to check if it is null. + +Change-Id: Id407a71ca8eccd60a713c34429e7e3f16e2cdd12 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1847,6 +1847,11 @@ int nat46_ipv6_input(struct sk_buff *old + int l3_infrag_payload_len = ntohs(ip6h->payload_len); + int check_for_l4 = 0; + ++ if (nat46 == NULL) { ++ printk("nat46:%p skb is dropped for no valid instance found\n", old_skb); ++ return err; ++ } ++ + nat46debug(4, "nat46_ipv6_input packet"); + + if(ip6_input_not_interested(nat46, ip6h, old_skb)) { +@@ -2353,6 +2358,11 @@ int nat46_ipv4_input(struct sk_buff *old + + char v6saddr[16], v6daddr[16]; + ++ if (nat46 == NULL) { ++ printk("nat46:%p skb is dropped for no valid instance found\n", old_skb); ++ return err; ++ } ++ + memset(v6saddr, 1, 16); + memset(v6daddr, 2, 16); + diff --git a/package/kernel/nat46/patches/112-fix_icmp_crash.patch b/package/kernel/nat46/patches/112-fix_icmp_crash.patch new file mode 100644 index 00000000000000..5d34697a45b84d --- /dev/null +++ b/package/kernel/nat46/patches/112-fix_icmp_crash.patch @@ -0,0 +1,40 @@ +Author: Pavithra R +Date: Wed Aug 5 20:57:33 2020 +0530 + +Fix crash of free skb + +This patch is propagated from the 4.4 kernel commit +b959b0d45c66ae004a5bfc1687980093fa5b8cc3. + +This is caused by the translation of the inner ipv6 header, it +move memory by the inner head's tot_len which is not exact that +inner packet will be trimmed for icmp error packets size no more +than 576. + +Change-Id: Id5d41fa0721acdf6ea76721c45415fe3be432207 +Signed-off-by: Pavithra R + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -2245,7 +2245,9 @@ static uint16_t xlate_pkt_in_err_v4_to_v + + if (skb_tailroom(old_skb) >= IPV6V4HDRDELTA){ + skb_put(old_skb, IPV6V4HDRDELTA); +- memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ntohs(iiph->tot_len) - IPV4HDRSIZE); ++ /* ErrorICMP size is less than 576, the inner ipv4 packet will be trimmed */ ++ memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ++ ntohs(iph->tot_len) - 2 * IPV4HDRSIZE - sizeof(struct icmphdr)); + memcpy(iiph, &ip6h, IPV6HDRSIZE); + } + else { +@@ -2258,7 +2260,9 @@ static uint16_t xlate_pkt_in_err_v4_to_v + + skb_put(old_skb, IPV6V4HDRDELTA); + iiph = (struct iphdr *)(icmp_hdr(old_skb) + 1); +- memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ntohs(iiph->tot_len) - IPV4HDRSIZE); ++ /* ErrorICMP size is less than 576, the inner ipv4 packet will be trimmed */ ++ memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), ++ ntohs(iph->tot_len) - 2 * IPV4HDRSIZE - sizeof(struct icmphdr)); + memcpy(iiph, &ip6h, IPV6HDRSIZE); + nat46 = get_nat46_instance(old_skb); + iph = ip_hdr(old_skb); diff --git a/package/kernel/nat46/patches/116-rate-limit-the-print.patch b/package/kernel/nat46/patches/116-rate-limit-the-print.patch new file mode 100644 index 00000000000000..d857d0e6cb0a05 --- /dev/null +++ b/package/kernel/nat46/patches/116-rate-limit-the-print.patch @@ -0,0 +1,34 @@ +Author: Pavithra R +Date: Wed Sep 30 14:05:50 2020 +0530 + +nat46: Add rate limit to a print. + +This patch is propagated from the kernel 4.4 commit +d47f62508d2c105f236470e56bedbe279db0e6f1 + +Change-Id: I2119fbe54d630c3ed39535f1cb1b8a0d9d3199b4 +Signed-off-by: Pavithra R +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -1928,7 +1928,9 @@ int nat46_ipv6_input(struct sk_buff *old + memcpy(&hdr6.saddr, &ip6h_inner->daddr, 16); + memcpy(&hdr6.daddr, &ip6h_inner->saddr, 16); + if (!pairs_xlate_v6_to_v4_outer(nat46, &apair, &hdr6, proto, &v4saddr, &v4daddr)) { +- nat46debug(0, "[nat46] Could not translate v6->v4"); ++ if (net_ratelimit()) { ++ nat46debug(0, "[nat46] Could not translate v6->v4"); ++ } + goto done; + } + v4saddr = apair->local.v4_pref; +@@ -2436,7 +2438,9 @@ int nat46_ipv4_input(struct sk_buff *old + } + + if(!pairs_xlate_v4_to_v6_outer(nat46, &apair, hdr4, having_l4 ? &sport : NULL, having_l4 ? &dport : NULL, v6saddr, v6daddr)) { +- nat46debug(0, "[nat46] Could not translate v4->v6"); ++ if (net_ratelimit()) { ++ nat46debug(0, "[nat46] Could not translate v4->v6"); ++ } + goto done; + } + diff --git a/package/kernel/nat46/patches/117-fix-icmp-no-payload-bug.patch b/package/kernel/nat46/patches/117-fix-icmp-no-payload-bug.patch new file mode 100644 index 00000000000000..a8bff4a945457c --- /dev/null +++ b/package/kernel/nat46/patches/117-fix-icmp-no-payload-bug.patch @@ -0,0 +1,34 @@ +Author: Pavithra R +Date: Wed Sep 30 14:27:37 2020 +0530 + +nat46: Fix for ICMP error packets with no payload. + +This patch is propagated from the kernel 4.4 commit +d8b29a8e31f948a5d7338aa69c36e0f654fcb9e4 + +When no payload is attached to the original packet, any +ICMP error message generated in response to such packets +gets dropped due to malformed packet at CE. + +During the translation of packet-in-error in ICMP, +the IPv6 header in ICMPv6 payload gets corrupted. +Hence, the translated packet gets dropped at CE. + +This fix updates the outer IPv4 header's total length +before translating to IPv6 header. + +Change-Id: Ifd9802afb50771de39b4c6fb734d36b0801613ec +Signed-off-by: Pavithra R +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -2266,9 +2266,8 @@ static uint16_t xlate_pkt_in_err_v4_to_v + memmove(((char *)iiph + IPV6HDRSIZE), (iiph + 1), + ntohs(iph->tot_len) - 2 * IPV4HDRSIZE - sizeof(struct icmphdr)); + memcpy(iiph, &ip6h, IPV6HDRSIZE); +- nat46 = get_nat46_instance(old_skb); +- iph = ip_hdr(old_skb); + } ++ iph->tot_len = htons(ntohs(iph->tot_len) + IPV6V4HDRDELTA); + + /* Swapping Ports for outer header */ + /* Another work-around till LPM is not present. */ diff --git a/package/kernel/nat46/patches/118-performance_fix.patch b/package/kernel/nat46/patches/118-performance_fix.patch new file mode 100644 index 00000000000000..5de4906ef99f7f --- /dev/null +++ b/package/kernel/nat46/patches/118-performance_fix.patch @@ -0,0 +1,415 @@ +Author: Suruchi Agarwal +Date: Fri Dec 17 13:37:15 2021 -0800 + + nat46: Performance fix + + Avoid allocating new skb and copy for map-t translation + + Change-Id: I621b90609b4642d64b6e4cfb98b105b3fcbb0365 + Signed-off-by: Suruchi Agarwal + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -118,7 +118,7 @@ char *get_next_arg(char **ptail) { + return pc; + } + +-/* ++/* + * Parse an IPv6 address (if pref_len is NULL), or prefix (if it isn't). + * parses destructively (places \0 between address and prefix len) + */ +@@ -163,7 +163,7 @@ int try_parse_ipv4_prefix(u32 *v4addr, i + } + + +-/* ++/* + * parse a rule argument and put config into a rule. + * advance the tail to prepare for the next arg parsing. + * destructive. +@@ -384,7 +384,7 @@ char *xlate_style_to_string(nat46_xlate_ + return "unknown"; + } + +-/* ++/* + * Get the nat46 configuration into a supplied buffer (if non-null). + */ + int nat46_get_ipair_config(nat46_instance_t *nat46, int ipair, char *buf, int count) { +@@ -985,6 +985,28 @@ __sum16 csum_ipv6_unmagic(nat46_instance + return csum; + } + ++/* Update UDP with incremental checksum */ ++__sum16 csum_ipv6_udp_remagic(struct ipv6hdr *ip6hdr, u32 csum) { ++ uint32_t sum; ++ sum = csum_partial(ip6hdr->saddr.s6_addr16, 2 * sizeof(ip6hdr->saddr), ~csum); ++ sum = ((sum >> 16) & 0xffff) + (sum & 0xffff); ++ sum += ((sum >> 16) & 0xffff); ++ return (u16)(~sum); ++} ++ ++/* Undo the IPv4 pseudoheader inclusion into the checksum */ ++__sum16 csum_ipv4_unmagic(__be32 saddr, __be32 daddr, ++ u32 csum) { ++ u32 s; ++ uint32_t addr_csum; ++ csum = ntohs(~csum); ++ addr_csum = (saddr & 0xffff) + (saddr >> 16) + (daddr & 0xffff) + (daddr >> 16); ++ s= csum + ntohs(~addr_csum); ++ s = ((s >> 16) & 0xffff) + (s & 0xffff); ++ s += ((s >> 16) & 0xffff); ++ return htons((u16)(~s)); ++} ++ + /* Update ICMPv6 type/code with incremental checksum adjustment */ + void update_icmp6_type_code(nat46_instance_t *nat46, struct icmp6hdr *icmp6h, u8 type, u8 code) { + u16 old_tc = *((u16 *)icmp6h); +@@ -1038,9 +1060,8 @@ void fill_v6hdr_from_v4hdr(struct iphdr + ip6h->hop_limit = iph->ttl; + } + +-void fill_v4hdr_from_v6hdr(struct iphdr * iph, struct ipv6hdr *ip6h, __u32 v4saddr, __u32 v4daddr, __u16 id, __u16 frag_off, __u16 proto, int l3_payload_len) { +- uint32_t ver_class_flow = ntohl(*(__be32 *)ip6h); +- iph->ttl = ip6h->hop_limit; ++void fill_v4hdr_from_v6hdr(struct iphdr * iph, uint32_t ver_class_flow, uint8_t hop_limit, __u32 v4saddr, __u32 v4daddr, __u16 id, __u16 frag_off, __u16 proto, int l3_payload_len) { ++ iph->ttl = hop_limit; + iph->saddr = v4saddr; + iph->daddr = v4daddr; + iph->protocol = proto; +@@ -1198,6 +1219,8 @@ void pairs_xlate_v6_to_v4_inner(nat46_in + */ + int xlate_payload6_to4(nat46_instance_t *nat46, void *pv6, void *ptrans_hdr, int v6_len, u16 *ul_sum, int *ptailTruncSize) { + struct ipv6hdr *ip6h = pv6; ++ uint32_t ver_class_flow; ++ uint8_t hop_limit; + __u32 v4saddr, v4daddr; + struct iphdr new_ipv4; + struct iphdr *iph = &new_ipv4; +@@ -1274,7 +1297,10 @@ int xlate_payload6_to4(nat46_instance_t + } + } + +- fill_v4hdr_from_v6hdr(iph, ip6h, v4saddr, v4daddr, ipid, ipflags, proto, infrag_payload_len); ++ ver_class_flow = ntohl(*(__be32 *)ip6h); ++ hop_limit = ip6h->hop_limit; ++ ++ fill_v4hdr_from_v6hdr(iph, ver_class_flow, hop_limit, v4saddr, v4daddr, ipid, ipflags, proto, infrag_payload_len); + if(ul_sum) { + *ul_sum = unchecksum16(pv6, (((u8 *)ptrans_hdr)-((u8 *)pv6))/2, *ul_sum); + *ul_sum = rechecksum16(iph, 10, *ul_sum); +@@ -1831,6 +1857,8 @@ EXPORT_SYMBOL(xlate_6_to_4); + + int nat46_ipv6_input(struct sk_buff *old_skb) { + struct ipv6hdr *ip6h = ipv6_hdr(old_skb); ++ uint32_t ver_class_flow; ++ uint8_t hop_limit; + nat46_xlate_rulepair_t *apair; + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + uint16_t proto; +@@ -1839,22 +1867,20 @@ int nat46_ipv6_input(struct sk_buff *old + + struct iphdr * iph; + __u32 v4saddr, v4daddr; +- struct sk_buff * new_skb = 0; + int err = 0; +- int truncSize = 0; + int tailTruncSize = 0; + int v6packet_l3size = sizeof(*ip6h); + int l3_infrag_payload_len = ntohs(ip6h->payload_len); + int check_for_l4 = 0; + +- if (nat46 == NULL) { ++ if (unlikely(nat46 == NULL)) { + printk("nat46:%p skb is dropped for no valid instance found\n", old_skb); + return err; + } + + nat46debug(4, "nat46_ipv6_input packet"); + +- if(ip6_input_not_interested(nat46, ip6h, old_skb)) { ++ if(unlikely(ip6_input_not_interested(nat46, ip6h, old_skb))) { + nat46debug(1, "nat46_ipv6_input not interested"); + goto done; + } +@@ -1985,47 +2011,45 @@ int nat46_ipv6_input(struct sk_buff *old + } + } + +- new_skb = skb_copy(old_skb, GFP_ATOMIC); // other possible option: GFP_ATOMIC +- if (!new_skb) { +- nat46debug(0, "[nat46] Could not copy v6 skb"); +- goto done; +- } ++ ver_class_flow = ntohl(*(__be32 *)ip6h); ++ hop_limit = ip6h->hop_limit; + + /* Remove any debris in the socket control block */ +- memset(IPCB(new_skb), 0, sizeof(struct inet_skb_parm)); ++ memset(IPCB(old_skb), 0, sizeof(struct inet_skb_parm)); ++ + /* Remove netfilter references to IPv6 packet, new netfilter references will be created based on IPv4 packet */ + #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) +- nf_reset(new_skb); ++ nf_reset(old_skb); + #else +- skb_ext_reset(new_skb); +- nf_reset_ct(new_skb); ++ skb_ext_reset(old_skb); ++ nf_reset_ct(old_skb); + #endif + + /* modify packet: actual IPv6->IPv4 transformation */ +- truncSize = v6packet_l3size - sizeof(struct iphdr); /* chop first 20 bytes */ +- skb_pull(new_skb, truncSize); +- skb_put(new_skb, -tailTruncSize); ++ skb_pull(old_skb, sizeof(struct iphdr)); + l3_infrag_payload_len -= tailTruncSize; +- skb_reset_network_header(new_skb); +- skb_set_transport_header(new_skb,IPV4HDRSIZE); /* transport (TCP/UDP/ICMP/...) header starts after 20 bytes */ ++ skb_reset_mac_header(old_skb); ++ skb_reset_network_header(old_skb); ++ skb_set_transport_header(old_skb,IPV4HDRSIZE); /* transport (TCP/UDP/ICMP/...) header starts after 20 bytes */ + + /* build IPv4 header */ +- iph = ip_hdr(new_skb); +- fill_v4hdr_from_v6hdr(iph, ip6h, v4saddr, v4daddr, frag_id, frag_off, proto, l3_infrag_payload_len); +- new_skb->protocol = htons(ETH_P_IP); ++ iph = ip_hdr(old_skb); ++ fill_v4hdr_from_v6hdr(iph, ver_class_flow, hop_limit, v4saddr, v4daddr, frag_id, frag_off, proto, l3_infrag_payload_len); ++ old_skb->protocol = htons(ETH_P_IP); + + if (ntohs(iph->tot_len) >= 2000) { + nat46debug(0, "Too big IP len: %d", ntohs(iph->tot_len)); + } + +- nat46debug(5, "about to send v4 packet, flags: %02x", IPCB(new_skb)->flags); +- nat46_netdev_count_xmit(new_skb, old_skb->dev); ++ nat46debug(5, "about to send v4 packet, flags: %02x", IPCB(old_skb)->flags); ++ nat46_netdev_count_xmit(old_skb, old_skb->dev); + +- /* set skb->iif */ +- new_skb->skb_iif = old_skb->skb_iif; +- +- netif_rx(new_skb); ++ netif_rx(old_skb); + ++ /* ++ * skb was consumed in the ipv4 format, don't release later. ++ */ ++ err = 1; + /* TBD: should copy be released here? */ + + done: +@@ -2056,7 +2080,7 @@ bool nat46_get_info(struct net_device *d + } + EXPORT_SYMBOL(nat46_get_info); + +-void ip6_update_csum(struct sk_buff * skb, struct ipv6hdr * ip6hdr, int do_atomic_frag) ++void ip6_update_csum(struct sk_buff * skb, struct ipv6hdr * ip6hdr, uint32_t v4saddr, uint32_t v4daddr, int do_atomic_frag) + { + u32 sum1=0; + u16 sum2=0; +@@ -2079,12 +2103,13 @@ void ip6_update_csum(struct sk_buff * sk + struct udphdr *udp = udp_hdr(skb); + unsigned udplen = ntohs(ip6hdr->payload_len) - (do_atomic_frag?8:0); /* UDP hdr + payload */ + +- oldsum = udp->check; +- udp->check = 0; +- +- sum1 = csum_partial((char*)udp, udplen, 0); /* calculate checksum for UDP hdr+payload */ +- sum2 = csum_ipv6_magic(&ip6hdr->saddr, &ip6hdr->daddr, udplen, ip6hdr->nexthdr, sum1); /* add pseudoheader */ +- ++ if (!udp->check) { ++ sum1 = csum_partial((char*)udp, udplen, 0); /* calculate checksum for UDP hdr+payload */ ++ sum2 = csum_ipv6_magic(&ip6hdr->saddr, &ip6hdr->daddr, udplen, ip6hdr->nexthdr, sum1); /* add pseudoheader */ ++ } else { ++ sum1 = csum_ipv4_unmagic(v4saddr, v4daddr, udp->check); ++ sum2 = csum_ipv6_udp_remagic(ip6hdr, sum1); ++ } + udp->check = sum2; + + break; +@@ -2348,7 +2373,6 @@ static uint16_t nat46_get_ce_port(nat46_ + int nat46_ipv4_input(struct sk_buff *old_skb) { + nat46_instance_t *nat46 = get_nat46_instance(old_skb); + nat46_xlate_rulepair_t apair; +- struct sk_buff *new_skb; + uint16_t sport = 0, dport = 0, ret = 0; + + int err = 0; +@@ -2360,10 +2384,15 @@ int nat46_ipv4_input(struct sk_buff *old + + struct ipv6hdr * hdr6; + struct iphdr * hdr4 = ip_hdr(old_skb); ++ uint32_t v4saddr, v4daddr; ++ uint8_t ttl; ++ uint16_t tot_len; ++ uint8_t protocol; ++ uint16_t frag_off, id; + + char v6saddr[16], v6daddr[16]; + +- if (nat46 == NULL) { ++ if (unlikely(nat46 == NULL)) { + printk("nat46:%p skb is dropped for no valid instance found\n", old_skb); + return err; + } +@@ -2443,31 +2472,39 @@ int nat46_ipv4_input(struct sk_buff *old + goto done; + } + +- new_skb = skb_copy(old_skb, GFP_ATOMIC); +- if (!new_skb) { +- nat46debug(0, "[nat46] Could not copy v4 skb"); +- goto done; +- } ++ v4saddr = hdr4->saddr; ++ v4daddr = hdr4->daddr; ++ protocol = hdr4->protocol; ++ tot_len = hdr4->tot_len; ++ ttl = hdr4->ttl; ++ frag_off = hdr4->frag_off; ++ id = hdr4->id; + + /* Remove any debris in the socket control block */ +- memset(IP6CB(new_skb), 0, sizeof(struct inet6_skb_parm)); ++ memset(IP6CB(old_skb), 0, sizeof(struct inet6_skb_parm)); + /* Remove netfilter references to IPv4 packet, new netfilter references will be created based on IPv6 packet */ + #if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) +- nf_reset(new_skb); ++ nf_reset(old_skb); + #else +- skb_ext_reset(new_skb); +- nf_reset_ct(new_skb); ++ skb_ext_reset(old_skb); ++ nf_reset_ct(old_skb); + #endif + + /* expand header (add 20 extra bytes at the beginning of sk_buff) */ +- pskb_expand_head(new_skb, IPV6HDRSIZE - (hdr4->ihl << 2) + (add_frag_header?8:0), 0, GFP_ATOMIC); ++ if (skb_headroom(old_skb) < IPV6V4HDRDELTA) { ++ ret = pskb_expand_head(old_skb, IPV6V4HDRDELTA + (add_frag_header?8:0), 0, GFP_ATOMIC); ++ if (unlikely(ret)) { ++ nat46debug(0, "[nat46] Could not expand skb header"); ++ goto done; ++ } ++ } + +- skb_push(new_skb, IPV6HDRSIZE - (hdr4->ihl << 2) + (add_frag_header?8:0)); /* push boundary by extra 20 bytes */ ++ skb_push(old_skb, IPV6HDRSIZE - (hdr4->ihl << 2) + (add_frag_header?8:0)); /* push boundary by extra 20 bytes */ + +- skb_reset_network_header(new_skb); +- skb_set_transport_header(new_skb, IPV6HDRSIZE + (add_frag_header?8:0) ); /* transport (TCP/UDP/ICMP/...) header starts after 40 bytes */ ++ skb_reset_network_header(old_skb); ++ skb_set_transport_header(old_skb, IPV6HDRSIZE + (add_frag_header?8:0) ); /* transport (TCP/UDP/ICMP/...) header starts after 40 bytes */ + +- hdr6 = ipv6_hdr(new_skb); ++ hdr6 = ipv6_hdr(old_skb); + memset(hdr6, 0, sizeof(*hdr6) + (add_frag_header?8:0)); + + /* build IPv6 header */ +@@ -2475,13 +2512,14 @@ int nat46_ipv4_input(struct sk_buff *old + *(__be32 *)hdr6 = htonl(0x60000000 | (tclass << 20)) | flowlabel; /* version, priority, flowlabel */ + + /* IPv6 length is a payload length, IPv4 is hdr+payload */ +- hdr6->payload_len = htons(ntohs(hdr4->tot_len) - (hdr4->ihl << 2) + (add_frag_header?8:0)); +- hdr6->nexthdr = hdr4->protocol; +- hdr6->hop_limit = hdr4->ttl; ++ hdr6->payload_len = htons(ntohs(tot_len) - sizeof(struct iphdr) + (add_frag_header?8:0)); ++ hdr6->nexthdr = protocol; ++ hdr6->hop_limit = ttl; ++ + memcpy(&hdr6->saddr, v6saddr, 16); + memcpy(&hdr6->daddr, v6daddr, 16); + +- new_skb->protocol = htons(ETH_P_IPV6); ++ old_skb->protocol = htons(ETH_P_IPV6); + + if (add_frag_header) { + struct frag_hdr *fh = (struct frag_hdr*)(hdr6 + 1); +@@ -2490,8 +2528,8 @@ int nat46_ipv4_input(struct sk_buff *old + /* Flag to represent whether PSID is assigned to MAP-T node or not */ + bool is_psid = false; + +- fh->frag_off = htons(((ntohs(hdr4->frag_off) >> 13) & 7) + ((ntohs(hdr4->frag_off) & 0x1FFF) << 3)); +- fh->nexthdr = hdr4->protocol; ++ fh->frag_off = htons(((ntohs(frag_off) >> 13) & 7) + ((ntohs(frag_off) & 0x1FFF) << 3)); ++ fh->nexthdr = protocol; + + /* + * PSID assigned MAP-T node will have non-zero ea_len and we are currently +@@ -2506,29 +2544,30 @@ int nat46_ipv4_input(struct sk_buff *old + if (ce_port_num) { + fh->identification = htonl(ce_port_num); + } else { +- fh->identification = htonl(ntohs(hdr4->id)); ++ fh->identification = htonl(ntohs(id)); + } + } else { +- fh->identification = htonl(ntohs(hdr4->id)); ++ fh->identification = htonl(ntohs(id)); + } + + + } +- ip6_update_csum(new_skb, hdr6, add_frag_header); ++ ip6_update_csum(old_skb, hdr6, v4saddr, v4daddr, add_frag_header); + +- hdr6->nexthdr = add_frag_header ? NEXTHDR_FRAGMENT : hdr4->protocol; ++ hdr6->nexthdr = add_frag_header ? NEXTHDR_FRAGMENT : protocol; + + + // FIXME: check if you can not fit the packet into the cached MTU +- // if (dst_mtu(skb_dst(new_skb))==0) { } +- +- nat46debug(5, "about to send v6 packet, flags: %02x", IP6CB(new_skb)->flags); +- nat46_netdev_count_xmit(new_skb, old_skb->dev); ++ // if (dst_mtu(skb_dst(old_skb))==0) { } + +- /* set skb->iif */ +- new_skb->skb_iif = old_skb->skb_iif; ++ nat46debug(5, "about to send v6 packet, flags: %02x", IPCB(old_skb)->flags); ++ nat46_netdev_count_xmit(old_skb, old_skb->dev); ++ netif_rx(old_skb); + +- netif_rx(new_skb); ++ /* ++ * skb was reused, needn't free it later. ++ */ ++ err = 1; + + done: + release_nat46_instance(nat46); +--- a/nat46/modules/nat46-core.h ++++ b/nat46/modules/nat46-core.h +@@ -39,7 +39,7 @@ + #define IPV4HDRSIZE 20 + #define IPV6V4HDRDELTA (IPV6HDRSIZE - IPV4HDRSIZE) + +-/* ++/* + * A generic v4<->v6 translation structure. + * The currently supported translation styles: + */ +--- a/nat46/modules/nat46-netdev.c ++++ b/nat46/modules/nat46-netdev.c +@@ -100,8 +100,7 @@ static netdev_tx_t nat46_netdev_xmit(str + + if(ETH_P_IP == ntohs(skb->protocol)) { + ret = nat46_ipv4_input(skb); +- } +- if(ETH_P_IPV6 == ntohs(skb->protocol)) { ++ }else if(ETH_P_IPV6 == ntohs(skb->protocol)) { + ret = nat46_ipv6_input(skb); + } + if(0 == ret) { +@@ -174,6 +173,7 @@ static void nat46_netdev_setup(struct ne + dev->mtu = 16384; /* iptables does reassembly. Rather than using ETH_DATA_LEN, let's try to get as much mileage as we can with the Linux stack */ + dev->features = NETIF_F_NETNS_LOCAL; + dev->flags = IFF_NOARP | IFF_POINTOPOINT; ++ dev->priv_flags_ext = IFF_EXT_MAPT; + } + + int nat46_netdev_create(char *basename, struct net_device **dev) diff --git a/package/kernel/nat46/patches/120-sleeping_backtrace.patch b/package/kernel/nat46/patches/120-sleeping_backtrace.patch new file mode 100644 index 00000000000000..5963af2b0365d7 --- /dev/null +++ b/package/kernel/nat46/patches/120-sleeping_backtrace.patch @@ -0,0 +1,57 @@ +commit 9457a8be6e700f39e6b545f8db0edd30c0693700 +Author: Ken Zhu +Date: Tue Sep 6 11:11:20 2022 -0700 + + nat46: fix sleeping warning back trace + + use spin_lock instead of mutex_lock since + mutex_lock could sleep in the kernel packet process. + + Change-Id: I65c15a9f618ef296159884a0d6d742e66aaf6623 + Signed-off-by: Ken Zhu + +--- a/nat46/modules/nat46-glue.c ++++ b/nat46/modules/nat46-glue.c +@@ -18,7 +18,7 @@ + #include "nat46-glue.h" + #include "nat46-core.h" + +-static DEFINE_MUTEX(ref_lock); ++static DEFINE_SPINLOCK(ref_lock); + int is_valid_nat46(nat46_instance_t *nat46) { + return (nat46 && (nat46->sig == NAT46_SIGNATURE)); + } +@@ -47,28 +47,27 @@ nat46_instance_t *alloc_nat46_instance(i + return nat46; + } + +- + nat46_instance_t *get_nat46_instance(struct sk_buff *sk) { + nat46_instance_t *nat46 = netdev_nat46_instance(sk->dev); +- mutex_lock(&ref_lock); ++ spin_lock_bh(&ref_lock); + if (is_valid_nat46(nat46)) { + nat46->refcount++; +- mutex_unlock(&ref_lock); ++ spin_unlock_bh(&ref_lock); + return nat46; + } else { + printk("[nat46] get_nat46_instance: Could not find a valid NAT46 instance!"); +- mutex_unlock(&ref_lock); ++ spin_unlock_bh(&ref_lock); + return NULL; + } + } + + void release_nat46_instance(nat46_instance_t *nat46) { +- mutex_lock(&ref_lock); ++ spin_lock_bh(&ref_lock); + nat46->refcount--; + if(0 == nat46->refcount) { + printk("[nat46] release_nat46_instance: freeing nat46 instance with %d pairs\n", nat46->npairs); + nat46->sig = FREED_NAT46_SIGNATURE; + kfree(nat46); + } +- mutex_unlock(&ref_lock); ++ spin_unlock_bh(&ref_lock); + } diff --git a/package/kernel/nat46/patches/121-tos-fix.patch b/package/kernel/nat46/patches/121-tos-fix.patch new file mode 100644 index 00000000000000..6b265886f5b70b --- /dev/null +++ b/package/kernel/nat46/patches/121-tos-fix.patch @@ -0,0 +1,27 @@ +Author: Ramkishan Gurjar +Date: Thu Nov 16 15:30:04 2023 +0530 + + nat46: Fix traffic class is not set in ipv6 Header from ipv4 tos value. + + Change-Id: I781d7af8bc9751dd23f6c3f4195644b3f9025fcb + Signed-off-by: Ramkishan Gurjar + +--- a/nat46/modules/nat46-core.c ++++ b/nat46/modules/nat46-core.c +@@ -2397,6 +2397,8 @@ int nat46_ipv4_input(struct sk_buff *old + return err; + } + ++ tclass = ip_tos_ignore ? 0 : hdr4->tos; /* traffic class */ ++ + memset(v6saddr, 1, 16); + memset(v6daddr, 2, 16); + +@@ -2508,7 +2510,6 @@ int nat46_ipv4_input(struct sk_buff *old + memset(hdr6, 0, sizeof(*hdr6) + (add_frag_header?8:0)); + + /* build IPv6 header */ +- tclass = ip_tos_ignore ? 0 : hdr4->tos; /* traffic class */ + *(__be32 *)hdr6 = htonl(0x60000000 | (tclass << 20)) | flowlabel; /* version, priority, flowlabel */ + + /* IPv6 length is a payload length, IPv4 is hdr+payload */ diff --git a/package/kernel/qca-ssdk/Makefile b/package/kernel/qca-ssdk/Makefile index bbe9f120514627..ed18f17504dbef 100644 --- a/package/kernel/qca-ssdk/Makefile +++ b/package/kernel/qca-ssdk/Makefile @@ -1,13 +1,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=qca-ssdk -PKG_RELEASE:=6 +PKG_RELEASE:=1 PKG_SOURCE_URL:=https://git.codelinaro.org/clo/qsdk/oss/lklm/qca-ssdk.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2023-10-04 -PKG_SOURCE_VERSION:=23a5aa4a4d5834da7a07efb58baebfbee91786b0 -PKG_MIRROR_HASH:=53fb201053b3aca004c4da07b06a0608b0b3322a2062b1f7ab3b3a7871ddabcb +PKG_SOURCE_DATE:=2024-04-17 +PKG_SOURCE_VERSION:=3d060f7ad70d087f6b0452abe79ab6d042e8cd53 +PKG_MIRROR_HASH:=6f5e390b294e699491584094f5d7eb941de6237ad8c5320191e9e306fbcd8eb5 PKG_FLAGS:=nonshared PKG_BUILD_PARALLEL:=1 diff --git a/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch b/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch deleted file mode 100644 index 2dc092326306b3..00000000000000 --- a/package/kernel/qca-ssdk/patches/0001-config-identify-kernel-6.6.patch +++ /dev/null @@ -1,47 +0,0 @@ -From f6c0115daaac586740e873a3b8145c5370a73dce Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Sat, 17 Feb 2024 13:02:31 +0100 -Subject: [PATCH] config: identify kernel 6.6 - -Identify kernel 6.6 so it can be compiled against. - -Signed-off-by: Robert Marko ---- - config | 5 +++++ - make/linux_opt.mk | 4 ++-- - 2 files changed, 7 insertions(+), 2 deletions(-) - ---- a/config -+++ b/config -@@ -27,6 +27,11 @@ endif - ifeq ($(KVER),$(filter 6.1%,$(KVER))) - OS_VER=6_1 - endif -+ -+ifeq ($(KVER),$(filter 6.6%,$(KVER))) -+ OS_VER=6_6 -+endif -+ - ifeq ($(KVER), 3.4.0) - OS_VER=3_4 - endif ---- a/make/linux_opt.mk -+++ b/make/linux_opt.mk -@@ -450,7 +450,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) - KASAN_SHADOW_SCALE_SHIFT := 3 - endif - -- ifeq ($(OS_VER),$(filter 5_4 6_1, $(OS_VER))) -+ ifeq ($(OS_VER),$(filter 5_4 6_1 6_6, $(OS_VER))) - ifeq ($(ARCH), arm64) - KASAN_OPTION += -DKASAN_SHADOW_SCALE_SHIFT=$(KASAN_SHADOW_SCALE_SHIFT) - endif -@@ -481,7 +481,7 @@ ifeq (KSLIB, $(MODULE_TYPE)) - - endif - -- ifeq ($(OS_VER),$(filter 4_4 5_4 6_1, $(OS_VER))) -+ ifeq ($(OS_VER),$(filter 4_4 5_4 6_1 6_6, $(OS_VER))) - MODULE_CFLAG += -DKVER34 - MODULE_CFLAG += -DKVER32 - MODULE_CFLAG += -DLNX26_22 diff --git a/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch b/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch index c27902c4ce1d8c..9d028992a7298b 100644 --- a/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch +++ b/package/kernel/qca-ssdk/patches/101-hsl_phy-add-support-for-detection-PSGMII-PHY-mode.patch @@ -13,7 +13,7 @@ Signed-off-by: Christian Marangi --- a/src/hsl/phy/hsl_phy.c +++ b/src/hsl/phy/hsl_phy.c -@@ -1335,6 +1335,9 @@ hsl_port_phydev_interface_mode_status_ge +@@ -1322,6 +1322,9 @@ hsl_port_phydev_interface_mode_status_ge case PHY_INTERFACE_MODE_10GKR: *interface_mode_status = PORT_10GBASE_R; break; diff --git a/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch b/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch index 5e390d8ee339c0..db84ea1422f1b8 100644 --- a/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch +++ b/package/kernel/qca-ssdk/patches/102-qca-ssdk-support-selecting-PCS-channel-for-PORT3-on-.patch @@ -24,15 +24,15 @@ Signed-off-by: Mantas Pucka --- a/include/init/ssdk_dts.h +++ b/include/init/ssdk_dts.h -@@ -101,6 +101,7 @@ typedef struct +@@ -99,6 +99,7 @@ typedef struct a_uint32_t emu_chip_ver; /*only valid when is_emulation is true*/ a_uint32_t clk_mode; a_uint32_t pcie_hw_base; + a_uint32_t port3_pcs_channel; + led_ctrl_pattern_t source_pattern[SSDK_MAX_PORT_NUM][PORT_LED_SOURCE_MAX]; } ssdk_dt_cfg; - #define SSDK_MAX_NR_ETH 6 -@@ -162,6 +163,7 @@ a_uint32_t ssdk_device_id_get(a_uint32_t +@@ -161,6 +162,7 @@ a_uint32_t ssdk_device_id_get(a_uint32_t struct device_node *ssdk_dts_node_get(a_uint32_t dev_id); struct clk *ssdk_dts_essclk_get(a_uint32_t dev_id); struct clk *ssdk_dts_cmnclk_get(a_uint32_t dev_id); @@ -62,7 +62,7 @@ Signed-off-by: Mantas Pucka cppe_port_mux_ctrl.bf.port4_pcs_sel = --- a/src/adpt/hppe/adpt_hppe_uniphy.c +++ b/src/adpt/hppe/adpt_hppe_uniphy.c -@@ -1122,9 +1122,6 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin +@@ -1160,9 +1160,6 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin { a_uint32_t i; sw_error_t rv = SW_OK; @@ -72,7 +72,7 @@ Signed-off-by: Mantas Pucka union uniphy_mode_ctrl_u uniphy_mode_ctrl; -@@ -1134,9 +1131,7 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin +@@ -1172,9 +1169,7 @@ __adpt_hppe_uniphy_psgmii_mode_set(a_uin SSDK_DEBUG("uniphy %d is psgmii mode\n", uniphy_index); #if defined(CPPE) if (adpt_ppe_type_get(dev_id) == CPPE_TYPE) { @@ -92,14 +92,14 @@ Signed-off-by: Mantas Pucka +a_uint32_t ssdk_dts_port3_pcs_channel_get(a_uint32_t dev_id) +{ + ssdk_dt_cfg* cfg = ssdk_dt_global.ssdk_dt_switch_nodes[dev_id]; -+ ++ + return cfg->port3_pcs_channel; +} + - #ifndef BOARD_AR71XX #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) static void ssdk_dt_parse_mac_mode(a_uint32_t dev_id, -@@ -306,6 +313,25 @@ static void ssdk_dt_parse_mac_mode(a_uin + struct device_node *switch_node, ssdk_init_cfg *cfg) +@@ -305,6 +312,25 @@ static void ssdk_dt_parse_mac_mode(a_uin return; } @@ -109,7 +109,7 @@ Signed-off-by: Mantas Pucka +{ + const __be32 *port3_pcs_channel; + a_uint32_t len = 0; -+ ++ + port3_pcs_channel = of_get_property(switch_node, "port3_pcs_channel", &len); + if (!port3_pcs_channel) { + ssdk_dt_global.ssdk_dt_switch_nodes[dev_id]->port3_pcs_channel = 2; @@ -125,7 +125,7 @@ Signed-off-by: Mantas Pucka #ifdef IN_UNIPHY static void ssdk_dt_parse_uniphy(a_uint32_t dev_id) { -@@ -1292,6 +1318,7 @@ sw_error_t ssdk_dt_parse(ssdk_init_cfg * +@@ -1347,6 +1373,7 @@ sw_error_t ssdk_dt_parse(ssdk_init_cfg * rv = ssdk_dt_parse_access_mode(switch_node, ssdk_dt_priv); SW_RTN_ON_ERROR(rv); ssdk_dt_parse_mac_mode(*dev_id, switch_node, cfg); diff --git a/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch b/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch deleted file mode 100644 index 7ddca554ecbd50..00000000000000 --- a/package/kernel/qca-ssdk/patches/103-mdio-adapt-to-C22-and-C45-read-write-split.patch +++ /dev/null @@ -1,98 +0,0 @@ -From bdae481e89cbe551068a99028bb57119b59f5ff4 Mon Sep 17 00:00:00 2001 -From: Robert Marko -Date: Tue, 26 Mar 2024 12:19:49 +0100 -Subject: [PATCH] mdio: adapt to C22 and C45 read/write split - -Kernel 6.3 has introduced separate C45 read/write operations, and thus -split them out of the C22 operations completely so the old way of marking -C45 reads and writes via the register value does not work anymore. - -This is causing SSDK to fail and find C45 only PHY-s such as Aquantia ones: -[ 22.187877] ssdk_phy_driver_init[371]:INFO:dev_id = 0, phy_adress = 8, phy_id = 0x0 phytype doesn't match -[ 22.209924] ssdk_phy_driver_init[371]:INFO:dev_id = 0, phy_adress = 0, phy_id = 0x0 phytype doesn't match - -This in turn causes USXGMII MAC autoneg bit to not get set and then UNIPHY -autoneg will time out, causing the 10G ports not to work: -[ 37.292784] uniphy autoneg time out! - -So, lets detect C45 reads and writes by the magic BIT(30) in the register -argument and if so call separate C45 mdiobus read/write functions. - -Signed-off-by: Robert Marko ---- - include/init/ssdk_plat.h | 7 +++++++ - src/init/ssdk_plat.c | 30 ++++++++++++++++++++++++++++++ - 2 files changed, 37 insertions(+) - ---- a/include/init/ssdk_plat.h -+++ b/include/init/ssdk_plat.h -@@ -505,3 +505,10 @@ void ssdk_plat_exit(a_uint32_t dev_id); - - #endif - /*qca808x_end*/ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+#define MII_ADDR_C45 (1<<30) -+#define MII_DEVADDR_C45_SHIFT 16 -+#define MII_DEVADDR_C45_MASK GENMASK(20, 16) -+#define MII_REGADDR_C45_MASK GENMASK(15, 0) -+#endif ---- a/src/init/ssdk_plat.c -+++ b/src/init/ssdk_plat.c -@@ -356,6 +356,18 @@ phy_addr_validation_check(a_uint32_t phy - return A_TRUE; - } - -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+static inline u16 mdiobus_c45_regad(u32 regnum) -+{ -+ return FIELD_GET(MII_REGADDR_C45_MASK, regnum); -+} -+ -+static inline u16 mdiobus_c45_devad(u32 regnum) -+{ -+ return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); -+} -+#endif -+ - sw_error_t - qca_ar8327_phy_read(a_uint32_t dev_id, a_uint32_t phy_addr, - a_uint32_t reg, a_uint16_t* data) -@@ -371,9 +383,18 @@ qca_ar8327_phy_read(a_uint32_t dev_id, a - if (!bus) - return SW_NOT_SUPPORTED; - phy_addr = TO_PHY_ADDR(phy_addr); -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+ mutex_lock(&bus->mdio_lock); -+ if (reg & MII_ADDR_C45) -+ *data = __mdiobus_c45_read(bus, phy_addr, mdiobus_c45_devad(reg), mdiobus_c45_regad(reg)); -+ else -+ *data = __mdiobus_read(bus, phy_addr, reg); -+ mutex_unlock(&bus->mdio_lock); -+#else - mutex_lock(&bus->mdio_lock); - *data = __mdiobus_read(bus, phy_addr, reg); - mutex_unlock(&bus->mdio_lock); -+#endif - - return 0; - } -@@ -393,9 +414,18 @@ qca_ar8327_phy_write(a_uint32_t dev_id, - if (!bus) - return SW_NOT_SUPPORTED; - phy_addr = TO_PHY_ADDR(phy_addr); -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)) -+ mutex_lock(&bus->mdio_lock); -+ if (reg & MII_ADDR_C45) -+ __mdiobus_c45_write(bus, phy_addr, mdiobus_c45_devad(reg), mdiobus_c45_regad(reg), data); -+ else -+ __mdiobus_write(bus, phy_addr, reg, data); -+ mutex_unlock(&bus->mdio_lock); -+#else - mutex_lock(&bus->mdio_lock); - __mdiobus_write(bus, phy_addr, reg, data); - mutex_unlock(&bus->mdio_lock); -+#endif - - return 0; - } diff --git a/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch b/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch index 5635c2fdcfb307..6c28e0ff2ebd68 100644 --- a/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch +++ b/package/kernel/qca-ssdk/patches/200-allow-parallel-build.patch @@ -40,7 +40,7 @@ kslib_c: --- a/make/linux_opt.mk +++ b/make/linux_opt.mk -@@ -777,6 +777,6 @@ LOCAL_CFLAGS += $(CPU_CFLAG) -D"KBUILD_M +@@ -778,6 +778,6 @@ LOCAL_CFLAGS += $(CPU_CFLAG) -D"KBUILD_M #################################################################### # cflags for LNX Modules-Style Makefile #################################################################### diff --git a/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch b/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch new file mode 100644 index 00000000000000..5b57f41975bbca --- /dev/null +++ b/package/kernel/qca-ssdk/patches/201-fix-compile-warnings.patch @@ -0,0 +1,31 @@ +--- a/src/fal/fal_port_ctrl.c ++++ b/src/fal/fal_port_ctrl.c +@@ -2089,7 +2089,7 @@ fal_port_hibernate_get (a_uint32_t dev_i + */ + sw_error_t + fal_port_cdt (a_uint32_t dev_id, fal_port_t port_id, a_uint32_t mdi_pair, +- a_uint32_t * cable_status, a_uint32_t * cable_len) ++ fal_cable_status_t * cable_status, a_uint32_t * cable_len) + { + sw_error_t rv; + +--- a/src/fal/fal_portvlan.c ++++ b/src/fal/fal_portvlan.c +@@ -2173,7 +2173,7 @@ fal_netisolate_get(a_uint32_t dev_id, a_ + * @return SW_OK or error code + */ + sw_error_t +-fal_eg_trans_filter_bypass_en_set(a_uint32_t dev_id, a_bool_t enable) ++fal_eg_trans_filter_bypass_en_set(a_uint32_t dev_id, a_uint32_t enable) + { + sw_error_t rv; + +@@ -2190,7 +2190,7 @@ fal_eg_trans_filter_bypass_en_set(a_uint + * @return SW_OK or error code + */ + sw_error_t +-fal_eg_trans_filter_bypass_en_get(a_uint32_t dev_id, a_bool_t* enable) ++fal_eg_trans_filter_bypass_en_get(a_uint32_t dev_id, a_uint32_t* enable) + { + sw_error_t rv; + diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh index bc59c303b939ac..f7705f9ed2fe1c 100755 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless/mac80211.sh @@ -142,8 +142,9 @@ mac80211_hostapd_setup_base() { [ -n "$acs_exclude_dfs" ] && [ "$acs_exclude_dfs" -gt 0 ] && append base_cfg "acs_exclude_dfs=1" "$N" - json_get_vars noscan ht_coex min_tx_power:0 tx_burst + json_get_vars noscan ht_coex min_tx_power:0 json_get_values ht_capab_list ht_capab + json_get_vars tx_burst json_get_values channel_list channels [ "$auto_channel" = 0 ] && [ -z "$channel_list" ] && \ diff --git a/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh index e24a2a634ea606..4a3d6455b5eaa2 100644 --- a/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh +++ b/package/network/config/wifi-scripts/files/lib/wifi/mac80211.sh @@ -203,7 +203,7 @@ detect_mac80211() { set wireless.${name}.channel=${channel} set wireless.${name}.band=${mode_band} set wireless.${name}.htmode=$htmode - set wireless.${name}.disabled=1 + set wireless.${name}.disabled=0 set wireless.default_${name}=wifi-iface set wireless.default_${name}.device=${name} diff --git a/package/network/services/dnsmasq/files/dnsmasq.init b/package/network/services/dnsmasq/files/dnsmasq.init index 138a913f87f3d9..e75dfd13813082 100755 --- a/package/network/services/dnsmasq/files/dnsmasq.init +++ b/package/network/services/dnsmasq/files/dnsmasq.init @@ -8,6 +8,8 @@ PROG=/usr/sbin/dnsmasq ADD_LOCAL_DOMAIN=1 ADD_LOCAL_HOSTNAME=1 +ADD_LOCAL_IPV4_NAMESERVER=1 +ADD_LOCAL_IPV6_NAMESERVER=0 ADD_WAN_FQDN=0 ADD_LOCAL_FQDN="" @@ -1036,6 +1038,8 @@ dnsmasq_start() config_get_bool ADD_LOCAL_DOMAIN "$cfg" add_local_domain 1 config_get_bool ADD_LOCAL_HOSTNAME "$cfg" add_local_hostname 1 + config_get_bool ADD_LOCAL_IPV4_NAMESERVER "$cfg" add_local_ipv4_nameserver 1 + config_get_bool ADD_LOCAL_IPV6_NAMESERVER "$cfg" add_local_ipv6_nameserver 0 config_get ADD_LOCAL_FQDN "$cfg" add_local_fqdn "" config_get ADD_WAN_FQDN "$cfg" add_wan_fqdn 0 @@ -1192,8 +1196,12 @@ dnsmasq_start() [ $ADD_LOCAL_DOMAIN -eq 1 ] && [ -n "$DOMAIN" ] && { echo "search $DOMAIN" >> /tmp/resolv.conf } - DNS_SERVERS="$DNS_SERVERS 127.0.0.1" - [ -e /proc/sys/net/ipv6 ] && DNS_SERVERS="$DNS_SERVERS ::1" + [ "$ADD_LOCAL_IPV4_NAMESERVER" -eq 1 ] && { + DNS_SERVERS="$DNS_SERVERS 127.0.0.1" + } + [ -e /proc/sys/net/ipv6 ] && [ "$ADD_LOCAL_IPV6_NAMESERVER" -eq 1 ] && { + DNS_SERVERS="$DNS_SERVERS ::1" + } for DNS_SERVER in $DNS_SERVERS ; do echo "nameserver $DNS_SERVER" >> /tmp/resolv.conf done diff --git a/package/network/services/dnsmasq/patches/300-DHCPv6-Honor-assigning-IPv6-address-based-on-MAC-add.patch b/package/network/services/dnsmasq/patches/300-DHCPv6-Honor-assigning-IPv6-address-based-on-MAC-add.patch new file mode 100644 index 00000000000000..4dd7e22b34a7c4 --- /dev/null +++ b/package/network/services/dnsmasq/patches/300-DHCPv6-Honor-assigning-IPv6-address-based-on-MAC-add.patch @@ -0,0 +1,166 @@ +From 93ac8f9d469ff08d41170eb6934842b3626d5fdd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Wed, 23 Dec 2015 22:10:44 +0100 +Subject: [PATCH] DHCPv6: Honor assigning IPv6 address based on MAC address + +Currently IPv6 addresses are assigned to tuple (IAID, DUID). When system +changes IAID/DUID then old assigned IPv6 address cannot be reused, even +when in config file was DHCPv6 assignment based on MAC address (and not on +DUID). + +IAID/DUID is changed when rebooting from one operating system to another; +or after reinstalling system. In reality it is normal that DUID of some +machine is changed, so people rather assign also IPv6 addresses based on +MAC address. + +So assigning IPv6 based on MAC address in dnsmasq is currently semi-broken. + +This patch tries to fix it and honors IPv6 config rules with MAC address, +to always assign particular IPv6 address to specific MAC address (when +configured). And ignores the fact if IAID/DUID was changed. + +Normally IPv6 address should be assigned by IAID/DUID (which also state +DHCPv6 RFCs), but dnsmasq has already some support for assigning IPv6 +address based on MAC address, when users configured in config file. + +So this patch just tries to fix above problem for user configuration with +MAC addresses. It does not change assignment based on DUID. +--- + src/rfc3315.c | 55 +++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 47 insertions(+), 8 deletions(-) + +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -48,7 +48,7 @@ static int build_ia(struct state *state, + static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz); + static void mark_context_used(struct state *state, struct in6_addr *addr); + static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); +-static int check_address(struct state *state, struct in6_addr *addr); ++static int check_address(struct state *state, struct dhcp_config *config, struct in6_addr *addr); + static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now); + static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr); + static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, +@@ -688,8 +688,13 @@ static int dhcp6_no_relay(struct state * + } + else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) + continue; /* not an address we're allowed */ +- else if (!check_address(state, &req_addr)) ++ else if (!check_address(state, config, &req_addr)) + continue; /* address leased elsewhere */ ++ else if (state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type) && ++ match_netid(c->filter, solicit_tags, plain_range) && ++ !config_implies(config, c, &req_addr)) ++ continue; /* another static address is configured */ + + /* add address to output packet */ + add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now); +@@ -701,7 +706,10 @@ static int dhcp6_no_relay(struct state * + + /* Suggest configured address(es) */ + for (c = state->context; c; c = c->current) +- if (!(c->flags & CONTEXT_CONF_USED) && ++ if ((!(c->flags & CONTEXT_CONF_USED) || ++ (state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type) ++ )) && + match_netid(c->filter, solicit_tags, plain_range) && + config_valid(config, c, &addr, state, now)) + { +@@ -725,6 +733,11 @@ static int dhcp6_no_relay(struct state * + req_addr = ltmp->addr6; + if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range))) + { ++ if (state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type) && ++ match_netid(c->filter, solicit_tags, plain_range) && ++ !config_implies(config, c, &req_addr)) ++ continue; /* skip this lease because another static address is configured */ + add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now); + mark_context_used(state, &req_addr); + get_context_tag(state, c); +@@ -859,7 +872,7 @@ static int dhcp6_no_relay(struct state * + put_opt6_string(_("address unavailable")); + end_opt6(o1); + } +- else if (!check_address(state, &req_addr)) ++ else if (!check_address(state, config, &req_addr)) + { + /* Address leased to another DUID/IAID */ + o1 = new_opt6(OPTION6_STATUS_CODE); +@@ -989,6 +1002,16 @@ static int dhcp6_no_relay(struct state * + { + unsigned int lease_time; + ++ /* check if another static address is preferred */ ++ if (state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type) && ++ !config_implies(config, this_context, &req_addr)) ++ { ++ preferred_time = valid_time = 0; ++ message = _("deprecated"); ++ } ++ else ++ { + get_context_tag(state, this_context); + + if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME)) +@@ -1014,6 +1037,7 @@ static int dhcp6_no_relay(struct state * + + if (preferred_time == 0) + message = _("deprecated"); ++ } + + address_assigned = 1; + } +@@ -1070,11 +1094,22 @@ static int dhcp6_no_relay(struct state * + ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) + { + struct in6_addr req_addr; ++ struct dhcp_context *c; ++ int config_addr_ok = 1; + + /* alignment */ + memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); ++ ++ c = address6_valid(state->context, &req_addr, tagif, 1); ++ ++ if (c && state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type) && ++ !config_implies(config, c, &req_addr)) ++ { ++ config_addr_ok = 0; ++ } + +- if (!address6_valid(state->context, &req_addr, tagif, 1)) ++ if (!c || !config_addr_ok) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOTONLINK); +@@ -1692,11 +1727,15 @@ static void mark_config_used(struct dhcp + context->flags |= CONTEXT_CONF_USED; + } + +-/* make sure address not leased to another CLID/IAID */ +-static int check_address(struct state *state, struct in6_addr *addr) ++/* check that ipv6 address belongs to config with same mac address as in state or ipv6 address is not leased to another CLID/IAID */ ++static int check_address(struct state *state, struct dhcp_config *config, struct in6_addr *addr) + { + struct dhcp_lease *lease; + ++ if (state->mac_len && config && ++ config_has_mac(config, state->mac, state->mac_len, state->mac_type)) ++ return 1; ++ + if (!(lease = lease6_find_by_addr(addr, 128, 0))) + return 1; + +@@ -1773,7 +1812,7 @@ static int config_valid(struct dhcp_conf + { + setaddr6part(addr, addrpart+i); + +- if (check_address(state, addr)) ++ if (check_address(state, config, addr)) + return 1; + } + } diff --git a/package/network/utils/iproute2/patches/400-add-nss-qdisc.patch b/package/network/utils/iproute2/patches/400-add-nss-qdisc.patch new file mode 100644 index 00000000000000..299710f79169d0 --- /dev/null +++ b/package/network/utils/iproute2/patches/400-add-nss-qdisc.patch @@ -0,0 +1,2093 @@ +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -119,6 +119,251 @@ enum { + + #define TCA_STAB_MAX (__TCA_STAB_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 */ ++}; ++typedef enum tc_nsswred_weight_modes tc_nsswred_weight_mode_t; ++ ++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 */ ++}; ++ ++ + /* FIFO section */ + + struct tc_fifo_qopt { +--- a/tc/Makefile ++++ b/tc/Makefile +@@ -77,6 +77,7 @@ TCMODULES += q_etf.o + TCMODULES += q_taprio.o + TCMODULES += q_plug.o + TCMODULES += q_ets.o ++TCMODULES += q_nss.o + + TCSO := + +--- /dev/null ++++ b/tc/q_nss.c +@@ -0,0 +1,1826 @@ ++/* ++ ************************************************************************** ++ * Copyright (c) 2015, 2018 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 ++#include ++#include ++#include ++ ++#include "utils.h" ++#include "tc_util.h" ++#include "tc_red.h" ++ ++/* ======================== NSSWRED =======================*/ ++ ++static void nssred_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nssred limit BYTES avpkt BYTES [ min BYTES ] [ max BYTES ] [ probability VALUE ]\n"); ++ fprintf(stderr, " [ burst PACKETS ] [ecn] [ set_default ] [ accel_mode ]\n"); ++} ++ ++static void nsswred_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nsswred setup DPs NUMBER dp_default NUMBER [ weight_mode dscp ] [ecn] [ set_default ] [ accel_mode ]\n"); ++ fprintf(stderr, " nsswred limit BYTES DP NUMBER min BYTES max BYTES avpkt BYTES dscp NUMBER [ probability VALUE ] [ burst PACKETS ]\n"); ++} ++ ++static int nsswred_setup(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nsswred_qopt opt; ++ ++ memset(&opt, 0, sizeof(opt)); ++ unsigned int dps = 0; ++ unsigned int def_dp = 0; ++ bool accel_mode = false; ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "DPs") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&dps, *argv, 0) || dps > NSSWRED_CLASS_MAX) { ++ ++ fprintf(stderr, "DPs should be between 1 - %d\n", NSSWRED_CLASS_MAX); ++ return -1; ++ } ++ } else if (strcmp(*argv, "weight_mode") == 0) { ++ NEXT_ARG(); ++ if (strcmp(*argv, "dscp") == 0) { ++ opt.weight_mode = TC_NSSWRED_WEIGHT_MODE_DSCP; ++ } else { ++ fprintf(stderr, "Illegal \"weight_mode\", we only support dscp at this moment\n"); ++ } ++ } else if (strcmp(*argv, "ecn") == 0) { ++ opt.ecn = 1; ++ } else if (strcmp(*argv, "dp_default") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&def_dp, *argv, 0) || def_dp > dps) { ++ fprintf(stderr, "Illegal dp_default value\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "help") == 0) { ++ nsswred_explain(); ++ return -1; ++ } else if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsswred_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_NSS_FW; ++ } else if (opt.accel_mode != TCA_NSS_ACCEL_MODE_NSS_FW) { ++ fprintf(stderr, "accel_mode should be %d\n", TCA_NSS_ACCEL_MODE_NSS_FW); ++ return -1; ++ } ++ ++ if (!dps || !def_dp) { ++ fprintf(stderr, "Illegal nsswred setup parameters\n"); ++ return -1; ++ } ++ opt.traffic_classes = dps; ++ opt.def_traffic_class = def_dp; ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWRED_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nsswred_qopt opt; ++ ++ int total_args = argc; ++ unsigned burst = 0; ++ unsigned avpkt = 0; ++ double probability = 0.0; ++ unsigned char weighted = (strcmp(qu->id, "nsswred") == 0); ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "limit") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.limit, *argv)) { ++ fprintf(stderr, "Illegal \"limit\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "min") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.rap.min, *argv)) { ++ fprintf(stderr, "Illegal \"min\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "max") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.rap.max, *argv)) { ++ fprintf(stderr, "Illegal \"max\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "burst") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&burst, *argv, 0)) { ++ fprintf(stderr, "Illegal \"burst\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "avpkt") == 0) { ++ NEXT_ARG(); ++ if (get_size(&avpkt, *argv)) { ++ fprintf(stderr, "Illegal \"avpkt\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "probability") == 0) { ++ NEXT_ARG(); ++ if (sscanf(*argv, "%lg", &probability) != 1) { ++ fprintf(stderr, "Illegal \"probability\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "ecn") == 0) { ++ opt.ecn = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ if (weighted) { ++ nsswred_explain(); ++ } else { ++ nssred_explain(); ++ } ++ return -1; ++ } else if (weighted) { ++ if (strcmp(*argv, "setup") == 0) { ++ if (argc != total_args) { ++ fprintf(stderr, "Setup command must be the first parameter\n"); ++ return -1; ++ } ++ return nsswred_setup(qu, argc-1, argv+1, n); ++ } else if (strcmp(*argv, "DP") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&opt.traffic_id, *argv, 0)) { ++ fprintf(stderr, "Illegal \"DP\""); ++ return -1; ++ } ++ } else if (strcmp(*argv, "dscp") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&opt.weight_mode_value, *argv, 0)) { ++ fprintf(stderr, "Illegal \"dscp\" value\n"); ++ return -1; ++ } ++ } ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ if (weighted) { ++ nsswred_explain(); ++ } else { ++ nssred_explain(); ++ } ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "Accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ if (weighted) { ++ if (!opt.limit || !opt.rap.min || !opt.rap.max || !opt.traffic_id || !avpkt || !opt.weight_mode_value) { ++ fprintf(stderr, "Require limit, min, max, avpkt, DP, weight_mode_value\n"); ++ return -1; ++ } ++ } else { ++ if (!opt.limit || !avpkt) { ++ fprintf(stderr, "Require limit, avpkt"); ++ return -1; ++ } ++ } ++ ++ /* ++ * Compute default min/max thresholds based on ++ * Sally Floyd's recommendations: ++ * http://www.icir.org/floyd/REDparameters.txt ++ */ ++ if (!opt.rap.max) ++ opt.rap.max = opt.rap.min ? opt.rap.min * 3 : opt.limit / 4; ++ if (!opt.rap.min) ++ opt.rap.min = opt.rap.max / 3; ++ if (!burst) ++ burst = (2 * opt.rap.min + opt.rap.max) / (3 * avpkt); ++ if ((opt.rap.exp_weight_factor = tc_red_eval_ewma(opt.rap.min, burst, avpkt)) < 0) { ++ fprintf(stderr, "Failed to calculate EWMA constant.\n"); ++ return -1; ++ } ++ ++ /* ++ * project [0.0-1.0] to [0-255] to avoid floating point calculation ++ */ ++ opt.rap.probability = probability * (pow(2, 8)-1); ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWRED_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSWRED_MAX + 1]; ++ struct tc_nsswred_qopt *qopt; ++ int i; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSWRED_MAX, opt); ++ ++ if (tb[TCA_NSSWRED_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSWRED_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSWRED_PARMS]); ++ ++ if (strcmp(qu->id, "nsswred") == 0) { ++ fprintf(f, "DPs %d def_DP %d weight mode: " , qopt->traffic_classes, qopt->def_traffic_class); ++ if (qopt->weight_mode == TC_NSSWRED_WEIGHT_MODE_DSCP) ++ fprintf(f, "DSCP\n"); ++ else ++ fprintf(f, "Unknown\n"); ++ for (i = 0;i < qopt->traffic_classes; i ++) { ++ if (qopt->tntc[i].rap.exp_weight_factor) { ++ double prob = (double)qopt->tntc[i].rap.probability; ++ fprintf(f, "DP %d: limit %d, weight mode value: %d min: %d max: %d exp_weight_factor: %d probability %.2f\n", ++ i + 1, qopt->tntc[i].limit, qopt->tntc[i].weight_mode_value ++ , qopt->tntc[i].rap.min,qopt->tntc[i].rap.max,qopt->tntc[i].rap.exp_weight_factor,prob/255); ++ } ++ } ++ } else { ++ double prob = (double)qopt->rap.probability; ++ fprintf(f, "limit %d, min: %d max: %d exp_weight_factor: %d probability %.2f\n", ++ qopt->limit, qopt->rap.min,qopt->rap.max,qopt->rap.exp_weight_factor,prob/255); ++ } ++ ++ if (qopt->ecn) ++ fprintf(f, "ECN enabled "); ++ if (qopt->set_default) ++ fprintf(f, "set_default "); ++ ++ fprintf(f, "accel_mode: %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++struct qdisc_util nssred_qdisc_util = { ++ .id = "nssred", ++ .parse_qopt = nsswred_parse_opt, ++ .print_qopt = nsswred_print_opt, ++}; ++ ++struct qdisc_util nsswred_qdisc_util = { ++ .id = "nsswred", ++ .parse_qopt = nsswred_parse_opt, ++ .print_qopt = nsswred_print_opt, ++}; ++ ++/* ======================== NSSFIFO =======================*/ ++ ++static void nssfifo_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nsspfifo [ limit PACKETS ] [ set_default ] [ accel_mode ]\n"); ++} ++ ++static int nssfifo_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nssfifo_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "limit") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.limit, *argv) || opt.limit == 0) { ++ fprintf(stderr, "Illegal \"limit\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nssfifo_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nssfifo_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSFIFO_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssfifo_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSFIFO_MAX + 1]; ++ struct tc_nssfifo_qopt *qopt; ++ SPRINT_BUF(b1); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSFIFO_MAX, opt); ++ ++ if (tb[TCA_NSSFIFO_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSFIFO_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSFIFO_PARMS]); ++ ++ if (strcmp(qu->id, "nssbfifo") == 0) ++ fprintf(f, "limit %s ", sprint_size(qopt->limit, b1)); ++ else ++ fprintf(f, "limit %up ", qopt->limit); ++ ++ if (qopt->set_default) ++ fprintf(f, "set_default "); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++struct qdisc_util nsspfifo_qdisc_util = { ++ .id = "nsspfifo", ++ .parse_qopt = nssfifo_parse_opt, ++ .print_qopt = nssfifo_print_opt, ++}; ++ ++struct qdisc_util nssbfifo_qdisc_util = { ++ .id = "nssbfifo", ++ .parse_qopt = nssfifo_parse_opt, ++ .print_qopt = nssfifo_print_opt, ++}; ++ ++/* ======================== NSSFQ_CODEL =======================*/ ++ ++static void nssfq_codel_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nssfq_codel target TIME interval TIME [ flows NUMBER ] [ quantum BYTES ]" ++ "[ limit PACKETS ] [ set_default ] [ accel_mode ]\n"); ++} ++ ++static void nssfq_codel_explain_err1(void) ++{ ++ fprintf(stderr, "Value of target and interval should be greater than 1ms\n"); ++} ++ ++static int nssfq_codel_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nsscodel_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "target") == 0) { ++ NEXT_ARG(); ++ if (get_time(&opt.target, *argv)) { ++ fprintf(stderr, "Illegal \"target\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "limit") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.limit, *argv) || opt.limit == 0) { ++ fprintf(stderr, "Illegal \"limit\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "flows") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.flows, *argv) || opt.flows == 0) { ++ fprintf(stderr, "Illegal \"flows\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "quantum") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.quantum, *argv) || opt.quantum == 0) { ++ fprintf(stderr, "Illegal \"quantum\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "interval") == 0) { ++ NEXT_ARG(); ++ if (get_time(&opt.interval, *argv)) { ++ fprintf(stderr, "Illegal \"interval\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "ecn") == 0) { ++ fprintf(stderr, "Illegal, ECN not supported\n"); ++ nssfq_codel_explain(); ++ return -1; ++ } else if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nssfq_codel_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nssfq_codel_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_NSS_FW; ++ } else if (opt.accel_mode != TCA_NSS_ACCEL_MODE_NSS_FW) { ++ fprintf(stderr, "accel_mode should be %d\n", TCA_NSS_ACCEL_MODE_NSS_FW); ++ return -1; ++ } ++ ++ if (!opt.target || !opt.interval) { ++ nssfq_codel_explain(); ++ return -1; ++ } ++ ++ if (opt.target < 1000 || opt.interval < 1000) { ++ nssfq_codel_explain_err1(); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSCODEL_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssfq_codel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSCODEL_MAX + 1]; ++ struct tc_nsscodel_qopt *qopt; ++ SPRINT_BUF(b1); ++ SPRINT_BUF(b2); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSCODEL_MAX, opt); ++ ++ if (tb[TCA_NSSCODEL_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSCODEL_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSCODEL_PARMS]); ++ ++ fprintf(f, "target %s limit %up interval %s flows %u quantum %u ", ++ sprint_time(qopt->target, b1), ++ qopt->limit, ++ sprint_time(qopt->interval, b2), ++ qopt->flows, ++ qopt->quantum); ++ ++ if (qopt->ecn) ++ fprintf(f, "ecn "); ++ ++ if (qopt->set_default) ++ fprintf(f, "set_default "); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nssfq_codel_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) ++{ ++ struct tc_nssfq_codel_xstats *st; ++ ++ if (xstats == NULL) ++ return 0; ++ ++ if (RTA_PAYLOAD(xstats) < sizeof(*st)) ++ return -1; ++ ++ st = RTA_DATA(xstats); ++ fprintf(f, " maxpacket %u drop_overlimit %u new_flow_count %u ecn_mark %u\n", ++ st->maxpacket, st->drop_overlimit, st->new_flow_count, st->ecn_mark); ++ fprintf(f, " new_flows_len %u old_flows_len %u", st->new_flows_len, st->old_flows_len); ++ ++ return 0; ++} ++ ++struct qdisc_util nssfq_codel_qdisc_util = { ++ .id = "nssfq_codel", ++ .parse_qopt = nssfq_codel_parse_opt, ++ .print_qopt = nssfq_codel_print_opt, ++ .print_xstats = nssfq_codel_print_xstats, ++}; ++ ++/* ======================== NSSCODEL =======================*/ ++ ++static void nsscodel_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nsscodel target TIME interval TIME [ limit PACKETS ] [ set_default ] [ accel_mode ]\n"); ++} ++ ++static void nsscodel_explain_err1(void) ++{ ++ fprintf(stderr, "Value of target and interval should be greater than 1ms\n"); ++} ++ ++static int nsscodel_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nsscodel_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "target") == 0) { ++ NEXT_ARG(); ++ if (get_time(&opt.target, *argv)) { ++ fprintf(stderr, "Illegal \"target\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "limit") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.limit, *argv) || opt.limit == 0) { ++ fprintf(stderr, "Illegal \"limit\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "interval") == 0) { ++ NEXT_ARG(); ++ if (get_time(&opt.interval, *argv)) { ++ fprintf(stderr, "Illegal \"interval\"\n"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsscodel_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsscodel_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_NSS_FW; ++ } else if (opt.accel_mode != TCA_NSS_ACCEL_MODE_NSS_FW) { ++ fprintf(stderr, "accel_mode should be %d\n", TCA_NSS_ACCEL_MODE_NSS_FW); ++ return -1; ++ } ++ ++ if (!opt.target || !opt.interval) { ++ nsscodel_explain(); ++ return -1; ++ } ++ ++ if (opt.target < 1000 || opt.interval < 1000) { ++ nsscodel_explain_err1(); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSCODEL_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsscodel_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSCODEL_MAX + 1]; ++ struct tc_nsscodel_qopt *qopt; ++ SPRINT_BUF(b1); ++ SPRINT_BUF(b2); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSCODEL_MAX, opt); ++ ++ if (tb[TCA_NSSCODEL_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSCODEL_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSCODEL_PARMS]); ++ ++ fprintf(f, "target %s limit %up interval %s ", ++ sprint_time(qopt->target, b1), ++ qopt->limit, ++ sprint_time(qopt->interval, b2)); ++ ++ if (qopt->set_default) ++ fprintf(f, "set_default "); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nsscodel_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats) ++{ ++ struct tc_nsscodel_xstats *st; ++ ++ if (xstats == NULL) ++ return 0; ++ ++ if (RTA_PAYLOAD(xstats) < sizeof(*st)) ++ return -1; ++ ++ st = RTA_DATA(xstats); ++ fprintf(f, " peak queue delay %ums peak drop delay %ums", ++ st->peak_queue_delay, st->peak_drop_delay); ++ ++ return 0; ++} ++ ++struct qdisc_util nsscodel_qdisc_util = { ++ .id = "nsscodel", ++ .parse_qopt = nsscodel_parse_opt, ++ .print_qopt = nsscodel_print_opt, ++ .print_xstats = nsscodel_print_xstats, ++}; ++ ++/* ======================== NSSTBL =======================*/ ++ ++static void nsstbl_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nsstbl burst BYTES rate BPS [ mtu BYTES ] [ accel_mode ]\n"); ++} ++ ++static int nsstbl_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nsstbl_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "burst") == 0 || ++ strcmp(*argv, "buffer") == 0 || ++ strcmp(*argv, "maxburst") == 0) { ++ NEXT_ARG(); ++ if (opt.burst) { ++ fprintf(stderr, "Double \"buffer/burst\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.burst, *argv)) { ++ fprintf(stderr, "Illegal \"burst\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "mtu") == 0 || ++ strcmp(*argv, "minburst") == 0) { ++ NEXT_ARG(); ++ if (opt.mtu) { ++ fprintf(stderr, "Double \"mtu/minburst\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.mtu, *argv)) { ++ fprintf(stderr, "Illegal \"mtu\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "rate") == 0) { ++ NEXT_ARG(); ++ if (opt.rate) { ++ fprintf(stderr, "Double \"rate\" spec\n"); ++ return -1; ++ } ++ if (get_rate(&opt.rate, *argv)) { ++ fprintf(stderr, "Illegal \"rate\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsstbl_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsstbl_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ nsstbl_explain(); ++ return -1; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ if (!opt.rate || !opt.burst) { ++ fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n"); ++ return -1; ++ } ++ ++ /* ++ * Peakrate is currently not supported, but we keep the infrastructure ++ * for future use. However, we have disabled taking input for this. ++ */ ++ if (opt.peakrate) { ++ if (!opt.mtu) { ++ fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n"); ++ return -1; ++ } ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSTBL_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsstbl_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSTBL_MAX + 1]; ++ struct tc_nsstbl_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSTBL_MAX, opt); ++ ++ if (tb[TCA_NSSTBL_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSTBL_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSTBL_PARMS]); ++ ++ print_size(PRINT_FP, NULL, "buffer/maxburst %s ", qopt->burst); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ print_size(PRINT_FP, NULL, "mtu %s ", qopt->mtu); ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++struct qdisc_util nsstbl_qdisc_util = { ++ .id = "nsstbl", ++ .parse_qopt = nsstbl_parse_opt, ++ .print_qopt = nsstbl_print_opt, ++}; ++ ++/* ======================== NSSPRIO =======================*/ ++ ++static void nssprio_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nssprio [ bands NUMBER (default 256) ] [ accel_mode ]\n"); ++} ++ ++static int nssprio_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nssprio_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "bands") == 0) { ++ NEXT_ARG(); ++ if (get_unsigned(&opt.bands, *argv, 0)) { ++ fprintf(stderr, "Illegal \"limit\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nssprio_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nssprio_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ opt.bands = TCA_NSSPRIO_MAX_BANDS; ++ } else if (opt.bands > TCA_NSSPRIO_MAX_BANDS) { ++ nssprio_explain(); ++ return -1; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSPRIO_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSPRIO_MAX + 1]; ++ struct tc_nssprio_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSPRIO_MAX, opt); ++ ++ if (tb[TCA_NSSPRIO_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSPRIO_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSPRIO_PARMS]); ++ ++ fprintf(f, "bands %u ", qopt->bands); ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++struct qdisc_util nssprio_qdisc_util = { ++ .id = "nssprio", ++ .parse_qopt = nssprio_parse_opt, ++ .print_qopt = nssprio_print_opt, ++}; ++ ++/* ======================== NSSBF =======================*/ ++ ++static void nssbf_explain_qdisc(void) ++{ ++ fprintf(stderr, ++ "Usage: ... nssbf [ accel_mode ]\n" ++ ); ++} ++ ++static void nssbf_explain_class(void) ++{ ++ fprintf(stderr, "Usage: ... nssbf rate BPS burst BYTES [ mtu BYTES ]\n"); ++ fprintf(stderr, " [ quantum BYTES ]\n"); ++} ++ ++static void nssbf_explain1(char *arg) ++{ ++ fprintf(stderr, "NSSBF: Illegal \"%s\"\n", arg); ++} ++ ++static int nssbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct tc_nssbf_qopt opt; ++ struct rtattr *tail; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (matches(*argv, "default") == 0) { ++ NEXT_ARG(); ++ if (opt.defcls != 0) { ++ fprintf(stderr, "NSSBF: Double \"default\"\n"); ++ return -1; ++ } ++ if (get_u16(&opt.defcls, *argv, 16) < 0) { ++ nssbf_explain1("default"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (matches(*argv, "help") == 0) { ++ nssbf_explain_qdisc(); ++ return -1; ++ } else { ++ fprintf(stderr, "NSSBF: What is \"%s\" ?\n", *argv); ++ nssbf_explain_qdisc(); ++ return -1; ++ } ++ argc--, argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_NSS_FW; ++ } else if (opt.accel_mode != TCA_NSS_ACCEL_MODE_NSS_FW) { ++ fprintf(stderr, "accel_mode should be %d\n", TCA_NSS_ACCEL_MODE_NSS_FW); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSBF_QDISC_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssbf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSBF_MAX + 1]; ++ struct tc_nssbf_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSBF_MAX, opt); ++ ++ if (tb[TCA_NSSBF_QDISC_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSBF_QDISC_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSBF_QDISC_PARMS]); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nssbf_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nssbf_class_qopt opt; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "burst") == 0 || ++ strcmp(*argv, "buffer") == 0 || ++ strcmp(*argv, "maxburst") == 0) { ++ NEXT_ARG(); ++ if (opt.burst) { ++ fprintf(stderr, "Double \"buffer/burst\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.burst, *argv)) { ++ fprintf(stderr, "Illegal \"burst\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "mtu") == 0) { ++ NEXT_ARG(); ++ if (opt.mtu) { ++ fprintf(stderr, "Double \"mtu\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.mtu, *argv)) { ++ fprintf(stderr, "Illegal \"mtu\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "quantum") == 0) { ++ NEXT_ARG(); ++ if (opt.quantum) { ++ fprintf(stderr, "Double \"quantum\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.quantum, *argv)) { ++ fprintf(stderr, "Illegal \"quantum\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "rate") == 0) { ++ NEXT_ARG(); ++ if (opt.rate) { ++ fprintf(stderr, "Double \"rate\" spec\n"); ++ return -1; ++ } ++ if (get_rate(&opt.rate, *argv)) { ++ fprintf(stderr, "Illegal \"rate\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "help") == 0) { ++ nssbf_explain_class(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nssbf_explain_class(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ nssbf_explain_class(); ++ return -1; ++ } ++ ++ if (!opt.rate || !opt.burst) { ++ fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n"); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSBF_CLASS_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssbf_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSBF_MAX + 1]; ++ struct tc_nssbf_class_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSBF_MAX, opt); ++ ++ if (tb[TCA_NSSBF_CLASS_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSBF_CLASS_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSBF_CLASS_PARMS]); ++ ++ print_size(PRINT_FP, NULL, "burst %s ", qopt->burst); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ print_size(PRINT_FP, NULL, "quantum %s ", qopt->quantum); ++ print_size(PRINT_FP, NULL, "mtu %s ", qopt->mtu); ++ ++ return 0; ++} ++ ++struct qdisc_util nssbf_qdisc_util = { ++ .id = "nssbf", ++ .parse_qopt = nssbf_parse_opt, ++ .print_qopt = nssbf_print_opt, ++ .parse_copt = nssbf_parse_class_opt, ++ .print_copt = nssbf_print_class_opt, ++}; ++ ++/* ======================== NSSWRR =======================*/ ++ ++static void nsswrr_explain_qdisc(void) ++{ ++ fprintf(stderr, "Usage (qdisc): ... nsswrr [ accel_mode ]\n"); ++} ++ ++static void nsswrr_explain_class(void) ++{ ++ fprintf(stderr, "Usage (class): ... nsswrr quantum PACKETS ]\n"); ++} ++ ++static int nsswrr_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct tc_nsswrr_qopt opt; ++ bool accel_mode = false; ++ struct rtattr *tail; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (matches(*argv, "help") == 0) { ++ nsswrr_explain_qdisc(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\" ?\n", *argv); ++ nsswrr_explain_qdisc(); ++ return -1; ++ } ++ argc--, argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWRR_QDISC_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswrr_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSWRR_MAX + 1]; ++ struct tc_nsswrr_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSWRR_MAX, opt); ++ ++ if (tb[TCA_NSSWRR_QDISC_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSWRR_QDISC_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSWRR_QDISC_PARMS]); ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nsswrr_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nsswrr_class_qopt opt; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "quantum") == 0) { ++ NEXT_ARG(); ++ if (get_u32(&opt.quantum, *argv, 10)) { ++ fprintf(stderr, "Illegal \"quantum\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsswrr_explain_class(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsswrr_explain_class(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ nsswrr_explain_class(); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWRR_CLASS_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswrr_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSWRR_MAX + 1]; ++ struct tc_nsswrr_class_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSWRR_MAX, opt); ++ ++ if (tb[TCA_NSSWRR_CLASS_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSWRR_CLASS_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSWRR_CLASS_PARMS]); ++ ++ fprintf(f, "quantum %up ", qopt->quantum); ++ return 0; ++} ++ ++struct qdisc_util nsswrr_qdisc_util = { ++ .id = "nsswrr", ++ .parse_qopt = nsswrr_parse_opt, ++ .print_qopt = nsswrr_print_opt, ++ .parse_copt = nsswrr_parse_class_opt, ++ .print_copt = nsswrr_print_class_opt, ++}; ++ ++/* ======================== NSSWFQ =======================*/ ++ ++static void nsswfq_explain_qdisc(void) ++{ ++ fprintf(stderr, "Usage (qdisc): ... nsswfq [ accel_mode ]\n"); ++} ++ ++static void nsswfq_explain_class(void) ++{ ++ fprintf(stderr, "Usage (class): ... nsswfq quantum BYTES ]\n"); ++} ++ ++static int nsswfq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct tc_nsswfq_qopt opt; ++ bool accel_mode = false; ++ struct rtattr *tail; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (matches(*argv, "help") == 0) { ++ nsswfq_explain_qdisc(); ++ return -1; ++ } else { ++ fprintf(stderr, "NSSWFQ: What is \"%s\" ?\n", *argv); ++ nsswfq_explain_qdisc(); ++ return -1; ++ } ++ argc--, argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWFQ_QDISC_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswfq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSWFQ_MAX + 1]; ++ struct tc_nsswfq_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSWFQ_MAX, opt); ++ ++ if (tb[TCA_NSSWFQ_QDISC_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSWFQ_QDISC_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSWFQ_QDISC_PARMS]); ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nsswfq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nsswfq_class_qopt opt; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "quantum") == 0) { ++ NEXT_ARG(); ++ if (get_size(&opt.quantum, *argv)) { ++ fprintf(stderr, "Illegal \"quantum\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsswfq_explain_class(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsswfq_explain_class(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ nsswfq_explain_class(); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSWFQ_CLASS_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsswfq_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSWFQ_MAX + 1]; ++ struct tc_nsswfq_class_qopt *qopt; ++ SPRINT_BUF(b1); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSWFQ_MAX, opt); ++ ++ if (tb[TCA_NSSWFQ_CLASS_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSWFQ_CLASS_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSWFQ_CLASS_PARMS]); ++ ++ fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1)); ++ ++ return 0; ++} ++ ++struct qdisc_util nsswfq_qdisc_util = { ++ .id = "nsswfq", ++ .parse_qopt = nsswfq_parse_opt, ++ .print_qopt = nsswfq_print_opt, ++ .parse_copt = nsswfq_parse_class_opt, ++ .print_copt = nsswfq_print_class_opt, ++}; ++ ++/* ======================== NSSHTB =======================*/ ++ ++static void nsshtb_explain_qdisc(void) ++{ ++ fprintf(stderr, ++ "Usage: ... nsshtb [ r2q ] [ accel_mode ]\n" ++ ); ++} ++ ++static void nsshtb_explain_class(void) ++{ ++ fprintf(stderr, "Usage: ... nsshtb priority 0-3 [ quantum BYTES ] [ rate BPS ] [ burst BYTES ] [crate BPS ] [ cburst BYTES ]\n"); ++ fprintf(stderr, " [ overhead BYTES ] \n"); ++} ++ ++static void nsshtb_explain1(char *arg) ++{ ++ fprintf(stderr, "NSSHTB: Illegal \"%s\"\n", arg); ++} ++ ++static int nsshtb_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct tc_nsshtb_qopt opt; ++ struct rtattr *tail; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "r2q") == 0) { ++ NEXT_ARG(); ++ if (opt.r2q != 0) { ++ fprintf(stderr, "NSSHTB: Double \"r2q\"\n"); ++ return -1; ++ } ++ if (get_u32(&opt.r2q, *argv, 10) < 0) { ++ nsshtb_explain1("r2q"); ++ return -1; ++ } ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsshtb_explain_qdisc(); ++ return -1; ++ } else { ++ fprintf(stderr, "NSSHTB: What is \"%s\" ?\n", *argv); ++ nsshtb_explain_qdisc(); ++ return -1; ++ } ++ argc--, argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSHTB_QDISC_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsshtb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSHTB_MAX + 1]; ++ struct tc_nsshtb_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSHTB_MAX, opt); ++ ++ if (tb[TCA_NSSHTB_QDISC_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSHTB_QDISC_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSHTB_QDISC_PARMS]); ++ ++ if (qopt->r2q != 0) ++ fprintf(f, "r2q %u ", qopt->r2q); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++static int nsshtb_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ int ok = 0; ++ struct rtattr *tail; ++ struct tc_nsshtb_class_qopt opt; ++ int crate = 0; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "burst") == 0) { ++ NEXT_ARG(); ++ if (opt.burst) { ++ fprintf(stderr, "Double \"burst\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.burst, *argv)) { ++ fprintf(stderr, "Illegal \"burst\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "rate") == 0) { ++ NEXT_ARG(); ++ if (opt.rate) { ++ fprintf(stderr, "Double \"rate\" spec\n"); ++ return -1; ++ } ++ if (get_rate(&opt.rate, *argv)) { ++ fprintf(stderr, "Illegal \"rate\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "cburst") == 0) { ++ NEXT_ARG(); ++ if (opt.cburst) { ++ fprintf(stderr, "Double \"cburst\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.cburst, *argv)) { ++ fprintf(stderr, "Illegal \"cburst\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "crate") == 0) { ++ NEXT_ARG(); ++ if (opt.crate) { ++ fprintf(stderr, "Double \"crate\" spec\n"); ++ return -1; ++ } ++ if (get_rate(&opt.crate, *argv)) { ++ fprintf(stderr, "Illegal \"crate\"\n"); ++ return -1; ++ } ++ crate++; ++ ok++; ++ } else if (strcmp(*argv, "priority") == 0) { ++ NEXT_ARG(); ++ if (opt.priority) { ++ fprintf(stderr, "Double \"priority\" spec\n"); ++ return -1; ++ } ++ if (get_u32(&opt.priority, *argv, 10) < 0) { ++ fprintf(stderr, "Illegal \"priority\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "quantum") == 0) { ++ NEXT_ARG(); ++ if (opt.quantum) { ++ fprintf(stderr, "Double \"quantum\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.quantum, *argv)) { ++ fprintf(stderr, "Illegal \"quantum\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "overhead") == 0) { ++ NEXT_ARG(); ++ if (opt.overhead) { ++ fprintf(stderr, "Double \"overhead\" spec\n"); ++ return -1; ++ } ++ if (get_size(&opt.overhead, *argv)) { ++ fprintf(stderr, "Illegal \"overhead\"\n"); ++ return -1; ++ } ++ ok++; ++ } else if (strcmp(*argv, "help") == 0) { ++ nsshtb_explain_class(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nsshtb_explain_class(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!ok) { ++ nsshtb_explain_class(); ++ return -1; ++ } ++ ++ if (opt.rate && !opt.burst) { ++ fprintf(stderr, "\"burst\" required if \"rate\" is specified.\n"); ++ return -1; ++ } ++ ++ if (!crate) { ++ fprintf(stderr, "\"crate\" is required.\n"); ++ return -1; ++ } ++ ++ if (opt.crate && !opt.cburst) { ++ fprintf(stderr, "\"cburst\" required if \"crate\" is non-zero.\n"); ++ return -1; ++ } ++ ++ if (opt.priority > 3) { ++ fprintf(stderr, "\"priority\" should be an integer between 0 and 3.\n"); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSHTB_CLASS_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nsshtb_print_class_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSHTB_MAX + 1]; ++ struct tc_nsshtb_class_qopt *qopt; ++ SPRINT_BUF(b1); ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSHTB_MAX, opt); ++ ++ if (tb[TCA_NSSHTB_CLASS_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSHTB_CLASS_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSHTB_CLASS_PARMS]); ++ ++ print_size(PRINT_FP, NULL, "burst %s ", qopt->burst); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ print_size(PRINT_FP, NULL, "cburst %s ", qopt->cburst); ++ tc_print_rate(PRINT_FP, NULL, "crate %s ", qopt->crate); ++ fprintf(f, "priority %u ", qopt->priority); ++ print_size(PRINT_FP, NULL, "quantum %s ", qopt->quantum); ++ print_size(PRINT_FP, NULL, "overhead %s ", qopt->overhead); ++ ++ return 0; ++} ++ ++struct qdisc_util nsshtb_qdisc_util = { ++ .id = "nsshtb", ++ .parse_qopt = nsshtb_parse_opt, ++ .print_qopt = nsshtb_print_opt, ++ .parse_copt = nsshtb_parse_class_opt, ++ .print_copt = nsshtb_print_class_opt, ++}; ++ ++/* ======================== NSSBLACKHOLE ======================= */ ++ ++static void nssblackhole_explain(void) ++{ ++ fprintf(stderr, "Usage: ... nssblackhole [ set_default ] [ accel_mode ]\n"); ++} ++ ++static int nssblackhole_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n) ++{ ++ struct rtattr *tail; ++ struct tc_nssblackhole_qopt opt; ++ bool accel_mode = false; ++ ++ memset(&opt, 0, sizeof(opt)); ++ ++ while (argc > 0) { ++ if (strcmp(*argv, "set_default") == 0) { ++ opt.set_default = 1; ++ } else if (strcmp(*argv, "accel_mode") == 0) { ++ NEXT_ARG(); ++ if (get_u8(&opt.accel_mode, *argv, 0)) { ++ fprintf(stderr, "Illegal accel_mode value\n"); ++ return -1; ++ } ++ accel_mode = true; ++ } else if (strcmp(*argv, "help") == 0) { ++ nssblackhole_explain(); ++ return -1; ++ } else { ++ fprintf(stderr, "What is \"%s\"?\n", *argv); ++ nssblackhole_explain(); ++ return -1; ++ } ++ argc--; argv++; ++ } ++ ++ if (!accel_mode) { ++ opt.accel_mode = TCA_NSS_ACCEL_MODE_PPE; ++ } else if (opt.accel_mode >= TCA_NSS_ACCEL_MODE_MAX) { ++ fprintf(stderr, "accel_mode should be < %d\n", TCA_NSS_ACCEL_MODE_MAX); ++ return -1; ++ } ++ ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, 1024, TCA_OPTIONS, NULL, 0); ++ addattr_l(n, 1024, TCA_NSSBLACKHOLE_PARMS, &opt, sizeof(opt)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ ++ return 0; ++} ++ ++static int nssblackhole_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSBLACKHOLE_MAX + 1]; ++ struct tc_nssblackhole_qopt *qopt; ++ ++ if (opt == NULL) ++ return 0; ++ ++ parse_rtattr_nested(tb, TCA_NSSBLACKHOLE_MAX, opt); ++ ++ if (tb[TCA_NSSBLACKHOLE_PARMS] == NULL) ++ return -1; ++ ++ if (RTA_PAYLOAD(tb[TCA_NSSBLACKHOLE_PARMS]) < sizeof(*qopt)) ++ return -1; ++ ++ qopt = RTA_DATA(tb[TCA_NSSBLACKHOLE_PARMS]); ++ ++ if (qopt->set_default) ++ fprintf(f, "set_default "); ++ ++ fprintf(f, "accel_mode %d ", qopt->accel_mode); ++ ++ return 0; ++} ++ ++struct qdisc_util nssblackhole_qdisc_util = { ++ .id = "nssblackhole", ++ .parse_qopt = nssblackhole_parse_opt, ++ .print_qopt = nssblackhole_print_opt, ++}; diff --git a/package/network/utils/iproute2/patches/500-add-nssmirred.patch b/package/network/utils/iproute2/patches/500-add-nssmirred.patch new file mode 100644 index 00000000000000..f4c3bc587d124c --- /dev/null +++ b/package/network/utils/iproute2/patches/500-add-nssmirred.patch @@ -0,0 +1,243 @@ +--- a/tc/Makefile ++++ b/tc/Makefile +@@ -50,6 +50,7 @@ TCMODULES += m_tunnel_key.o + TCMODULES += m_sample.o + TCMODULES += m_ct.o + TCMODULES += m_gate.o ++TCMODULES += m_nssmirred.o + TCMODULES += p_ip.o + TCMODULES += p_ip6.o + TCMODULES += p_icmp.o +--- /dev/null ++++ b/tc/m_nssmirred.c +@@ -0,0 +1,183 @@ ++/* ++ ************************************************************************** ++ * Copyright (c) 2019 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 ++#include ++#include ++#include "utils.h" ++#include "tc_util.h" ++#include "tc_common.h" ++#include ++ ++/* ++ * explain() ++ * API to print the explaination of nssmirred action statement's ++ * elements. ++ */ ++static void explain(void) ++{ ++ fprintf(stderr, "Usage: nssmirred redirect \n"); ++ fprintf(stderr, "where: \n"); ++ fprintf(stderr, "\tTO_DEVICENAME is the devicename to redirect to\n"); ++ fprintf(stderr, "\tFROM_DEVICENAME is the devicename to redirect from\n"); ++} ++ ++/* ++ * usage() ++ * API to show the usage of the nssmirred action. ++ */ ++static void usage(void) ++{ ++ explain(); ++ exit(-1); ++} ++ ++/* ++ * parse_nss_mirred() ++ * Parse and validate the nssmirred action statement. ++ */ ++static int parse_nss_mirred(struct action_util *a, int *argc_p, char ***argv_p, ++ int tca_id, struct nlmsghdr *n) ++{ ++ int idx, argc = *argc_p; ++ char **argv = *argv_p; ++ struct tc_nss_mirred p; ++ struct rtattr *tail; ++ ++ if (argc < 0) { ++ fprintf(stderr, "nssmirred bad argument count %d. Try option \"help\"\n", argc); ++ goto error; ++ } ++ ++ if (matches(*argv, "nssmirred")) { ++ fprintf(stderr, "nssmirred bad argument %s. Try option \"help\"\n", *argv); ++ goto error; ++ } ++ ++ NEXT_ARG(); ++ if (!matches(*argv, "help")) { ++ usage(); ++ } ++ ++ if (matches(*argv, "redirect")) { ++ fprintf(stderr, "nssmirred bad argument %s. Try option \"help\"\n", *argv); ++ goto error; ++ } ++ ++ NEXT_ARG(); ++ if (matches(*argv, "dev")) { ++ fprintf(stderr, "nssmirred: bad value %s. Try option \"help\"\n", *argv); ++ goto error; ++ } ++ ++ NEXT_ARG(); ++ memset(&p, 0, sizeof(struct tc_nss_mirred)); ++ if ((idx = ll_name_to_index(*argv)) == 0) { ++ fprintf(stderr, "Cannot find to device \"%s\"\n", *argv); ++ goto error; ++ } ++ ++ p.to_ifindex = idx; ++ NEXT_ARG(); ++ if (matches(*argv, "fromdev")) { ++ fprintf(stderr, "nssmirred: bad value %s. Try option \"help\"\n", *argv); ++ goto error; ++ } ++ ++ NEXT_ARG(); ++ if ((idx = ll_name_to_index(*argv)) == 0) { ++ fprintf(stderr, "Cannot find from device \"%s\"\n", *argv); ++ goto error; ++ } ++ ++ p.from_ifindex = idx; ++ p.action = TC_ACT_STOLEN; ++ tail = NLMSG_TAIL(n); ++ addattr_l(n, MAX_MSG, tca_id, NULL, 0); ++ addattr_l(n, MAX_MSG, TCA_NSS_MIRRED_PARMS, &p, sizeof (p)); ++ tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; ++ argc--; ++ argv++; ++ *argc_p = argc; ++ *argv_p = argv; ++ return 0; ++ ++error: ++ return -1; ++} ++ ++/* ++ * print_nss_mirred() ++ * Print information related to nssmirred action. ++ */ ++static int print_nss_mirred(struct action_util *au, FILE * f, struct rtattr *arg) ++{ ++ struct tc_nss_mirred *p; ++ struct rtattr *tb[TCA_NSS_MIRRED_MAX + 1]; ++ const char *from_dev, *to_dev; ++ ++ if (arg == NULL) { ++ return -1; ++ } ++ ++ parse_rtattr_nested(tb, TCA_NSS_MIRRED_MAX, arg); ++ ++ if (tb[TCA_NSS_MIRRED_PARMS] == NULL) { ++ fprintf(f, "[NULL nssmirred parameters]"); ++ goto error; ++ } ++ ++ p = RTA_DATA(tb[TCA_NSS_MIRRED_PARMS]); ++ if ((from_dev = ll_index_to_name(p->from_ifindex)) == 0) { ++ fprintf(stderr, "Invalid interface (index: %d)\n", p->from_ifindex); ++ goto error; ++ } ++ ++ if ((to_dev = ll_index_to_name(p->to_ifindex)) == 0) { ++ fprintf(stderr, "Invalid interface (index: %d)\n", p->to_ifindex); ++ goto error; ++ } ++ ++ fprintf(f, "nssmirred (%s to device %s) stolen\n", from_dev, to_dev); ++ fprintf(f, "\tindex %d ref %d bind %d\n",p->index,p->refcnt,p->bindcnt); ++ ++ if (show_stats) { ++ if (tb[TCA_NSS_MIRRED_TM]) { ++ struct tcf_t *tm = RTA_DATA(tb[TCA_NSS_MIRRED_TM]); ++ print_tm(f,tm); ++ } ++ } ++ return 0; ++ ++error: ++ return -1; ++} ++ ++/* ++ * nssmirred_action_util ++ * nssmirred action utility structure. ++ */ ++struct action_util nssmirred_action_util = { ++ .id = "nssmirred", ++ .parse_aopt = parse_nss_mirred, ++ .print_aopt = print_nss_mirred, ++}; +--- /dev/null ++++ b/include/linux/tc_act/tc_nssmirred.h +@@ -0,0 +1,44 @@ ++/* ++ ************************************************************************** ++ * Copyright (c) 2019 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 __LINUX_TC_NSS_MIR_H ++#define __LINUX_TC_NSS_MIR_H ++ ++#include ++#include ++ ++/* ++ * tc_nss_mirred ++ * Structure for nssmirred action. ++ */ ++struct tc_nss_mirred { ++ tc_gen; ++ __u32 from_ifindex; /* ifindex of the port to be redirected from */ ++ __u32 to_ifindex; /* ifindex of the port to be redirected to */ ++}; ++ ++/* ++ * Types of nssmirred action parameters. ++ */ ++enum { ++ TCA_NSS_MIRRED_UNSPEC, ++ TCA_NSS_MIRRED_TM, ++ TCA_NSS_MIRRED_PARMS, ++ __TCA_NSS_MIRRED_MAX ++}; ++#define TCA_NSS_MIRRED_MAX (__TCA_NSS_MIRRED_MAX - 1) ++ ++#endif /* __LINUX_TC_NSS_MIR_H */ diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1 index 70e87665b79a86..21e4230922efa1 100644 --- a/target/linux/generic/config-6.1 +++ b/target/linux/generic/config-6.1 @@ -861,6 +861,7 @@ CONFIG_BT_HCIUART_H4=y # CONFIG_BT_MSFTEXT is not set # CONFIG_BT_MTKSDIO is not set # CONFIG_BT_MTKUART is not set +# CONFIG_BT_QCOMSMD is not set # CONFIG_BT_RFCOMM is not set CONFIG_BT_RFCOMM_TTY=y # CONFIG_BT_SELFTEST is not set diff --git a/target/linux/qualcommax/config-6.6 b/target/linux/qualcommax/config-6.6 index 71b54041c50e59..3c3d0ae3617e57 100644 --- a/target/linux/qualcommax/config-6.6 +++ b/target/linux/qualcommax/config-6.6 @@ -18,6 +18,8 @@ CONFIG_ARCH_STACKWALK=y CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_ARCH_WANTS_NO_INSTR=y CONFIG_ARCH_WANTS_THP_SWAP=y +# CONFIG_ARM_SMMU_V3_PMU is not set +# CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU is not set CONFIG_ARM64=y CONFIG_ARM64_4K_PAGES=y CONFIG_ARM64_ERRATUM_1165522=y @@ -44,6 +46,8 @@ CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y CONFIG_ARM_AMBA=y CONFIG_ARM_ARCH_TIMER=y CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_SMMU_V3_PMU is not set +# CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU is not set CONFIG_ARM_GIC=y CONFIG_ARM_GIC_V2M=y CONFIG_ARM_GIC_V3=y @@ -78,11 +82,11 @@ CONFIG_COREDUMP=y CONFIG_CPUFREQ_DT=y CONFIG_CPUFREQ_DT_PLATDEV=y CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set CONFIG_CPU_FREQ_GOV_ATTR_SET=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y @@ -101,15 +105,15 @@ CONFIG_CRC8=y CONFIG_CRYPTO_AUTHENC=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_QCE=y -CONFIG_CRYPTO_DEV_QCE_AEAD=y +# CONFIG_CRYPTO_DEV_QCE=y +# CONFIG_CRYPTO_DEV_QCE_AEAD=y # CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set -CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y +# CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y # CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set # CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set -CONFIG_CRYPTO_DEV_QCE_SHA=y -CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 +# CONFIG_CRYPTO_DEV_QCE_SHA=y +# CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y +# CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 CONFIG_CRYPTO_DEV_QCOM_RNG=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_HASH_INFO=y @@ -125,6 +129,8 @@ CONFIG_CRYPTO_RNG=y CONFIG_CRYPTO_RNG2=y CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SM4_ARM64_CE_CCM is not set +# CONFIG_CRYPTO_SM4_ARM64_CE_GCM is not set CONFIG_CRYPTO_XTS=y CONFIG_CRYPTO_ZSTD=y CONFIG_DCACHE_WORD_ACCESS=y @@ -192,6 +198,11 @@ CONFIG_HAS_IOPORT=y CONFIG_HAS_IOPORT_MAP=y CONFIG_HWSPINLOCK=y CONFIG_HWSPINLOCK_QCOM=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_HZ=1000 +# CONFIG_HZ_100 is not set +CONFIG_HZ_1000=y CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y @@ -375,7 +386,12 @@ CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y CONFIG_POWER_RESET=y # CONFIG_POWER_RESET_MSM is not set CONFIG_POWER_SUPPLY=y -CONFIG_PREEMPT_NONE_BUILD=y +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +# CONFIG_PREEMPT_DYNAMIC is not set +# CONFIG_PREEMPT_NONE_BUILD is not set +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_RCU=y CONFIG_PRINTK_TIME=y CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_QCA807X_PHY=y @@ -504,6 +520,11 @@ CONFIG_SERIAL_MSM=y CONFIG_SERIAL_MSM_CONSOLE=y CONFIG_SGL_ALLOC=y CONFIG_SG_POOL=y +# CONFIG_ALLOC_SKB_PAGE_FRAG_DISABLE is not set +# CONFIG_DEBUG_OBJECTS_SKBUFF is not set +CONFIG_SKB_RECYCLER=y +CONFIG_SKB_RECYCLER_MULTI_CPU=y +# CONFIG_SKB_FAST_RECYCLABLE_DEBUG_ENABLE is not set CONFIG_SMP=y # CONFIG_SM_CAMCC_6350 is not set # CONFIG_SM_CAMCC_8450 is not set diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts index e62ae314fb5d82..4359c3d5b1c8fd 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts @@ -6,6 +6,7 @@ #include "ipq8074-512m.dtsi" #include "ipq8074-ac-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi index 6afafb35546e00..c2c5f4ce8362c0 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi @@ -4,6 +4,7 @@ #include "ipq8074-512m.dtsi" #include "ipq8074-ac-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts index d55904a24a447b..ba03e147fbf799 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-eap102.dts @@ -6,6 +6,7 @@ #include "ipq8074.dtsi" #include "ipq8074-ac-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts index 2fe723591e77f4..0c80020cd1b373 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-301w.dts @@ -6,6 +6,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts index ec66d47d16a93f..fe5a1367226177 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax9000.dts @@ -6,6 +6,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts index c5c089c00f7dd4..24dd51b47fc585 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-dl-wrx36.dts @@ -6,6 +6,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts index 289680d678be07..3c63c0b00350bc 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-haze.dts @@ -5,6 +5,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts index 0e71faea72a220..2c1e0f5e472fdb 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts @@ -3,6 +3,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax620.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax620.dts index ceb719d81322e1..66bf68eb656460 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax620.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wax620.dts @@ -5,6 +5,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts index 5b2c1d570fd943..ebc68e15a840e1 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts @@ -6,6 +6,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts index b18f38cc6cf4dc..1390b3a286b166 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nbg7815.dts @@ -9,6 +9,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi new file mode 100644 index 00000000000000..d85a37230a0e04 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/ { + nss_dummy_reg: nss-regulator { + compatible = "regulator-fixed"; + regulator-name = "nss-reg"; + regulator-min-microvolt = <848000>; + regulator-max-microvolt = <848000>; + regulator-always-on; + regulator-boot-on; + }; +}; + +&soc { + nss-common { + compatible = "qcom,nss-common"; + reg = <0x01868010 0x1000>; + reg-names = "nss-misc-reset"; + memory-region = <&nss_region>; + }; + + nss0: nss@40000000 { + compatible = "qcom,nss"; + interrupts = , + , + , + , + , + , + , + , + , + ; + reg = <0x39000000 0x1000>, + <0x38000000 0x30000>, + <0x0b111000 0x1000>; + reg-names = "nphys", "vphys", "qgic-phys"; + clocks = <&gcc GCC_NSS_NOC_CLK>, + <&gcc GCC_NSS_PTP_REF_CLK>, + <&gcc GCC_NSS_CSR_CLK>, + <&gcc GCC_NSS_CFG_CLK>, + <&gcc GCC_NSS_IMEM_CLK>, + <&gcc GCC_NSSNOC_QOSGEN_REF_CLK>, + <&gcc GCC_MEM_NOC_NSS_AXI_CLK>, + <&gcc GCC_NSSNOC_SNOC_CLK>, + <&gcc GCC_NSSNOC_TIMEOUT_REF_CLK>, + <&gcc GCC_NSS_CE_AXI_CLK>, + <&gcc GCC_NSS_CE_APB_CLK>, + <&gcc GCC_NSSNOC_CE_AXI_CLK>, + <&gcc GCC_NSSNOC_CE_APB_CLK>, + <&gcc GCC_NSSNOC_UBI0_AHB_CLK>, + <&gcc GCC_UBI0_CORE_CLK>, + <&gcc GCC_UBI0_AHB_CLK>, + <&gcc GCC_UBI0_AXI_CLK>, + <&gcc GCC_UBI0_MPT_CLK>, + <&gcc GCC_UBI0_NC_AXI_CLK>; + clock-names = "nss-noc-clk", + "nss-ptp-ref-clk", + "nss-csr-clk", + "nss-cfg-clk", + "nss-imem-clk", + "nss-nssnoc-qosgen-ref-clk", + "nss-mem-noc-nss-axi-clk", + "nss-nssnoc-snoc-clk", + "nss-nssnoc-timeout-ref-clk", + "nss-ce-axi-clk", + "nss-ce-apb-clk", + "nss-nssnoc-ce-axi-clk", + "nss-nssnoc-ce-apb-clk", + "nss-nssnoc-ahb-clk", + "nss-core-clk", + "nss-ahb-clk", + "nss-axi-clk", + "nss-mpt-clk", + "nss-nc-axi-clk"; + qcom,id = <0>; + qcom,num-queue = <4>; + qcom,num-irq = <10>; + qcom,num-pri = <4>; + qcom,load-addr = <0x40000000>; + qcom,low-frequency = <187200000>; + qcom,mid-frequency = <748800000>; + qcom,max-frequency = <1689600000>; + qcom,bridge-enabled; + qcom,ipv4-enabled; + qcom,ipv4-reasm-enabled; + qcom,ipv6-enabled; + qcom,ipv6-reasm-enabled; + qcom,wlanredirect-enabled; + qcom,tun6rd-enabled; + qcom,l2tpv2-enabled; + qcom,gre-enabled; + qcom,gre-redir-enabled; + qcom,gre-redir-mark-enabled; + qcom,map-t-enabled; + qcom,portid-enabled; + qcom,ppe-enabled; + qcom,pppoe-enabled; + qcom,pptp-enabled; + qcom,tunipip6-enabled; + qcom,shaping-enabled; + qcom,wlan-dataplane-offload-enabled; + qcom,vlan-enabled; + qcom,igs-enabled; + qcom,vxlan-enabled; + qcom,match-enabled; + qcom,mirror-enabled; + qcom,udp-st-enabled; + mx-supply = <&nss_dummy_reg>; + npu-supply = <&nss_dummy_reg>; + }; + + nss1: nss@40800000 { + compatible = "qcom,nss"; + interrupts = , + , + , + , + , + , + , + , + ; + reg = <0x39400000 0x1000>, + <0x38030000 0x30000>, + <0x0b111000 0x1000>; + reg-names = "nphys", "vphys", "qgic-phys"; + clocks = <&gcc GCC_NSS_NOC_CLK>, + <&gcc GCC_NSS_PTP_REF_CLK>, + <&gcc GCC_NSS_CSR_CLK>, + <&gcc GCC_NSS_CFG_CLK>, + <&gcc GCC_NSS_IMEM_CLK>, + <&gcc GCC_NSSNOC_QOSGEN_REF_CLK>, + <&gcc GCC_MEM_NOC_NSS_AXI_CLK>, + <&gcc GCC_NSSNOC_SNOC_CLK>, + <&gcc GCC_NSSNOC_TIMEOUT_REF_CLK>, + <&gcc GCC_NSS_CE_AXI_CLK>, + <&gcc GCC_NSS_CE_APB_CLK>, + <&gcc GCC_NSSNOC_CE_AXI_CLK>, + <&gcc GCC_NSSNOC_CE_APB_CLK>, + <&gcc GCC_NSSNOC_UBI1_AHB_CLK>, + <&gcc GCC_UBI1_CORE_CLK>, + <&gcc GCC_UBI1_AHB_CLK>, + <&gcc GCC_UBI1_AXI_CLK>, + <&gcc GCC_UBI1_MPT_CLK>, + <&gcc GCC_UBI1_NC_AXI_CLK>; + clock-names = "nss-noc-clk", + "nss-ptp-ref-clk", + "nss-csr-clk", + "nss-cfg-clk", + "nss-imem-clk", + "nss-nssnoc-qosgen-ref-clk", + "nss-mem-noc-nss-axi-clk", + "nss-nssnoc-snoc-clk", + "nss-nssnoc-timeout-ref-clk", + "nss-ce-axi-clk", + "nss-ce-apb-clk", + "nss-nssnoc-ce-axi-clk", + "nss-nssnoc-ce-apb-clk", + "nss-nssnoc-ahb-clk", + "nss-core-clk", + "nss-ahb-clk", + "nss-axi-clk", + "nss-mpt-clk", + "nss-nc-axi-clk"; + qcom,id = <1>; + qcom,num-queue = <4>; + qcom,num-irq = <9>; + qcom,num-pri = <4>; + qcom,load-addr = <0x40800000>; + qcom,capwap-enabled; + qcom,dtls-enabled; + qcom,tls-enabled; + qcom,crypto-enabled; + qcom,ipsec-enabled; + qcom,qvpn-enabled; + qcom,pvxlan-enabled; + qcom,clmap-enabled; + qcom,rmnet_rx-enabled; + }; + + nss_crypto: qcom,nss_crypto { + compatible = "qcom,nss-crypto"; + #address-cells = <1>; + #size-cells = <1>; + qcom,max-contexts = <64>; + qcom,max-context-size = <32>; + ranges; + + eip197_node { + compatible = "qcom,eip197"; + reg-names = "crypto_pbase"; + reg = <0x39800000 0x7ffff>; + clocks = <&gcc GCC_NSS_CRYPTO_CLK>, + <&gcc GCC_NSSNOC_CRYPTO_CLK>, + <&gcc GCC_CRYPTO_PPE_CLK>; + clock-names = "crypto_clk", + "crypto_nocclk", + "crypto_ppeclk"; + clock-frequency = /bits/ 64 <600000000 600000000 300000000>; + qcom,dma-mask = <0xff>; + qcom,transform-enabled; + qcom,aes128-cbc; + qcom,aes192-cbc; + qcom,aes256-cbc; + qcom,aes128-ctr; + qcom,aes192-ctr; + qcom,aes256-ctr; + qcom,aes128-ecb; + qcom,aes192-ecb; + qcom,aes256-ecb; + qcom,3des-cbc; + qcom,md5-hash; + qcom,sha160-hash; + qcom,sha224-hash; + qcom,sha384-hash; + qcom,sha512-hash; + qcom,sha256-hash; + qcom,md5-hmac; + qcom,sha160-hmac; + qcom,sha224-hmac; + qcom,sha256-hmac; + qcom,sha384-hmac; + qcom,sha512-hmac; + qcom,aes128-gcm-gmac; + qcom,aes192-gcm-gmac; + qcom,aes256-gcm-gmac; + qcom,aes128-cbc-md5-hmac; + qcom,aes128-cbc-sha160-hmac; + qcom,aes192-cbc-md5-hmac; + qcom,aes192-cbc-sha160-hmac; + qcom,aes256-cbc-md5-hmac; + qcom,aes256-cbc-sha160-hmac; + qcom,aes128-ctr-sha160-hmac; + qcom,aes192-ctr-sha160-hmac; + qcom,aes256-ctr-sha160-hmac; + qcom,aes128-ctr-md5-hmac; + qcom,aes192-ctr-md5-hmac; + qcom,aes256-ctr-md5-hmac; + qcom,3des-cbc-md5-hmac; + qcom,3des-cbc-sha160-hmac; + qcom,aes128-cbc-sha256-hmac; + qcom,aes192-cbc-sha256-hmac; + qcom,aes256-cbc-sha256-hmac; + qcom,aes128-ctr-sha256-hmac; + qcom,aes192-ctr-sha256-hmac; + qcom,aes256-ctr-sha256-hmac; + qcom,3des-cbc-sha256-hmac; + qcom,aes128-cbc-sha384-hmac; + qcom,aes192-cbc-sha384-hmac; + qcom,aes256-cbc-sha384-hmac; + qcom,aes128-ctr-sha384-hmac; + qcom,aes192-ctr-sha384-hmac; + qcom,aes256-ctr-sha384-hmac; + qcom,aes128-cbc-sha512-hmac; + qcom,aes192-cbc-sha512-hmac; + qcom,aes256-cbc-sha512-hmac; + qcom,aes128-ctr-sha512-hmac; + qcom,aes192-ctr-sha512-hmac; + qcom,aes256-ctr-sha512-hmac; + + engine0 { + reg_offset = <0x80000>; + qcom,ifpp-enabled; + qcom,ipue-enabled; + qcom,ofpp-enabled; + qcom,opue-enabled; + }; + }; + }; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rax120v2.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rax120v2.dts index ceb47f14fdd0a2..9fca32578d769a 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rax120v2.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rax120v2.dts @@ -4,6 +4,7 @@ #include "ipq8074.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include "ipq8074-hk-cpu.dtsi" #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wax630.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wax630.dts index 3393efd7b550f3..d241ae642acbac 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wax630.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wax630.dts @@ -5,6 +5,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts index d8237e81dde950..dfe27a99d18e64 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-wxr-5950ax12.dts @@ -5,6 +5,7 @@ #include "ipq8074.dtsi" #include "ipq8074-hk-cpu.dtsi" #include "ipq8074-ess.dtsi" +#include "ipq8074-nss.dtsi" #include #include #include diff --git a/target/linux/qualcommax/files/include/net/netfilter/nf_conntrack_dscpremark_ext.h b/target/linux/qualcommax/files/include/net/netfilter/nf_conntrack_dscpremark_ext.h new file mode 100644 index 00000000000000..dc6a5004ef6d7a --- /dev/null +++ b/target/linux/qualcommax/files/include/net/netfilter/nf_conntrack_dscpremark_ext.h @@ -0,0 +1,95 @@ +/* + ************************************************************************** + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 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 + +/* + * 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 */ + __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 */ diff --git a/target/linux/qualcommax/files/include/uapi/linux/tc_act/tc_nss_mirred.h b/target/linux/qualcommax/files/include/uapi/linux/tc_act/tc_nss_mirred.h new file mode 100644 index 00000000000000..3a368fcc8c17ee --- /dev/null +++ b/target/linux/qualcommax/files/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/qualcommax/files/net/core/skbuff_debug.c b/target/linux/qualcommax/files/net/core/skbuff_debug.c new file mode 100644 index 00000000000000..50d06759210995 --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_debug.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2013-2014, 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 "skbuff_debug.h" +#include "skbuff_notifier.h" +#include "skbuff_recycle.h" + +static int skbuff_debugobj_enabled __read_mostly = 1; + +static int skbuff_debug_event_handler(struct notifier_block *nb, + unsigned long action, void *data); +static struct notifier_block skbuff_debug_notify = { + .notifier_call = skbuff_debug_event_handler, + .priority = 0 +}; + +inline u32 skbuff_debugobj_sum(struct sk_buff *skb) +{ + int pos = offsetof(struct sk_buff, free_addr); + u32 sum = 0; + + while (pos--) + sum += ((u8 *)skb)[pos]; + + return sum; +} + +struct skbuff_debugobj_walking { + int pos; + void **d; +}; + +#ifdef CONFIG_ARM +static int skbuff_debugobj_walkstack(struct stackframe *frame, void *p) { + struct skbuff_debugobj_walking *w = (struct skbuff_debugobj_walking *)p; + unsigned long pc = frame->pc; + + if (w->pos < DEBUG_OBJECTS_SKBUFF_STACKSIZE - 1) { + w->d[w->pos++] = (void *)pc; + return 0; + } + + return -ENOENT; +} +#else +static bool skbuff_debugobj_walkstack(void *p, unsigned long pc) +{ + struct skbuff_debugobj_walking *w = (struct skbuff_debugobj_walking *)p; + + if (w->pos < DEBUG_OBJECTS_SKBUFF_STACKSIZE - 1) { + w->d[w->pos++] = (void *)pc; + return true; + } + + return false; +} +#endif + +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +static void skbuff_debugobj_get_stack(void **ret) +{ + struct skbuff_debugobj_walking w = {0, ret}; + void *p = &w; + +#ifdef CONFIG_ARM + struct stackframe frame; + register unsigned long current_sp asm ("sp"); + + frame.lr = (unsigned long)__builtin_return_address(0); + frame.fp = (unsigned long)__builtin_frame_address(0); + frame.sp = current_sp; + frame.pc = (unsigned long)skbuff_debugobj_get_stack; + + walk_stackframe(&frame, skbuff_debugobj_walkstack, p); +#else + arch_stack_walk(skbuff_debugobj_walkstack, p, current, NULL); +#endif + + ret[w.pos] = NULL; +} +#else +#error +static void skbuff_debugobj_get_stack(void **ret) +{ + /* not supported */ + ret[0] = 0xdeadbeef; +} +#endif + +void skbuff_debugobj_print_stack(void *const *stack) +{ + int i; + + for (i = 0; stack[i]; i++) + pr_emerg("\t %pS (0x%p)\n", stack[i], stack[i]); +} + +static const char *skbuff_debugobj_state_name(const struct sk_buff *skb) +{ + int obj_state; + + obj_state = debug_object_get_state((struct sk_buff *)skb); + switch (obj_state) { + case ODEBUG_STATE_NONE: + return "none"; + case ODEBUG_STATE_INIT: + return "init"; + case ODEBUG_STATE_INACTIVE: + return "inactive"; + case ODEBUG_STATE_ACTIVE: + return "active"; + case ODEBUG_STATE_DESTROYED: + return "destroyed"; + case ODEBUG_STATE_NOTAVAILABLE: + return "not available"; + default: + return "invalid"; + } +} + +void skbuff_debugobj_print_skb(const struct sk_buff *skb) +{ + pr_emerg("skb_debug: current process = %s (pid %i)\n", + current->comm, current->pid); + pr_emerg("skb_debug: skb 0x%p, next 0x%p, prev 0x%p, state = %s\n", skb, + skb->next, skb->prev, skbuff_debugobj_state_name(skb)); + pr_emerg("skb_debug: free stack:\n"); + skbuff_debugobj_print_stack(skb->free_addr); + pr_emerg("skb_debug: alloc stack:\n"); + skbuff_debugobj_print_stack(skb->alloc_addr); +} +EXPORT_SYMBOL(skbuff_debugobj_print_skb); + +/* skbuff_debugobj_fixup(): + * Called when an error is detected in the state machine for + * the objects + */ +static bool skbuff_debugobj_fixup(void *addr, enum debug_obj_state state) +{ + struct sk_buff *skb = (struct sk_buff *)addr; + ftrace_dump(DUMP_ALL); + WARN(1, "skb_debug: state = %d, skb = 0x%p sum = %d (now %d)\n", + state, skb, skb->sum, skbuff_debugobj_sum(skb)); + skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_FSM, skb); + + return true; +} + +static struct debug_obj_descr skbuff_debug_descr = { + .name = "sk_buff_struct", + .fixup_init = skbuff_debugobj_fixup, + .fixup_activate = skbuff_debugobj_fixup, + .fixup_destroy = skbuff_debugobj_fixup, + .fixup_free = skbuff_debugobj_fixup, +}; + +inline void skbuff_debugobj_activate(struct sk_buff *skb) +{ + int ret = 0; + + if (!skbuff_debugobj_enabled) + return; + + skbuff_debugobj_get_stack(skb->alloc_addr); + ret = debug_object_activate(skb, &skbuff_debug_descr); + if (ret) + goto err_act; + + skbuff_debugobj_sum_validate(skb); + + return; + +err_act: + ftrace_dump(DUMP_ALL); + WARN(1, "skb_debug: failed to activate err = %d skb = 0x%p sum = %d (now %d)\n", + ret, skb, skb->sum, skbuff_debugobj_sum(skb)); + skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_DBLALLOC, skb); +} + +inline void skbuff_debugobj_init_and_activate(struct sk_buff *skb) +{ + if (!skbuff_debugobj_enabled) + return; + + /* if we're coming from the slab, the skb->sum might + * be invalid anyways + */ + skb->sum = skbuff_debugobj_sum(skb); + + debug_object_init(skb, &skbuff_debug_descr); + skbuff_debugobj_activate(skb); +} + +inline void skbuff_debugobj_deactivate(struct sk_buff *skb) +{ + int obj_state; + + if (!skbuff_debugobj_enabled) + return; + + skb->sum = skbuff_debugobj_sum(skb); + + obj_state = debug_object_get_state(skb); + + if (obj_state == ODEBUG_STATE_ACTIVE) { + debug_object_deactivate(skb, &skbuff_debug_descr); + skbuff_debugobj_get_stack(skb->free_addr); + return; + } + + ftrace_dump(DUMP_ALL); + WARN(1, "skb_debug: deactivating inactive object skb=0x%p state=%d sum = %d (now %d)\n", + skb, obj_state, skb->sum, skbuff_debugobj_sum(skb)); + skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_DBLFREE, skb); +} + +inline void _skbuff_debugobj_sum_validate(struct sk_buff *skb, + const char *var, const char *src, + int line, const char *fxn) +{ + if (!skbuff_debugobj_enabled || !skb) + return; + + if (skb->sum == skbuff_debugobj_sum(skb)) + return; + + ftrace_dump(DUMP_ALL); + WARN(1, "skb_debug: skb sum changed skb = 0x%p sum = %d (now %d)\n", + skb, skb->sum, skbuff_debugobj_sum(skb)); + pr_emerg("skb_debug: %s() checking %s in %s:%d\n", fxn, var, src, line); + skb_recycler_notifier_send_event(SKB_RECYCLER_NOTIFIER_SUMERR, skb); +} + +inline void skbuff_debugobj_sum_update(struct sk_buff *skb) +{ + if (!skbuff_debugobj_enabled || !skb) + return; + + skb->sum = skbuff_debugobj_sum(skb); +} + +inline void skbuff_debugobj_destroy(struct sk_buff *skb) +{ + if (!skbuff_debugobj_enabled) + return; + + debug_object_destroy(skb, &skbuff_debug_descr); +} + +static int __init disable_object_debug(char *str) +{ + skbuff_debugobj_enabled = 0; + + pr_info("skb_debug: debug objects is disabled\n"); + return 0; +} + +early_param("no_skbuff_debug_objects", disable_object_debug); + +void skbuff_debugobj_print_skb_list(const struct sk_buff *skb_list, + const char *list_title, int cpu) +{ + int count; + struct sk_buff *skb_i = (struct sk_buff *)skb_list; + u32 sum_i, sum_now; + int obj_state; + + if (cpu < 0) { + cpu = get_cpu(); + put_cpu(); + } + pr_emerg("skb_debug: start skb list '%s' [CPU#%d]\n", list_title, cpu); + count = 0; + if (skb_list) { + do { + obj_state = + debug_object_get_state(skb_i); + if (obj_state < ODEBUG_STATE_NOTAVAILABLE) { + sum_i = skb_i->sum; + sum_now = skbuff_debugobj_sum(skb_i); + } else { + sum_i = 0; + sum_now = 0; + } + if (sum_i != sum_now) { + pr_emerg("skb_debug: [%02d] skb 0x%p, next 0x%p, prev 0x%p, state %d (%s), sum %d (now %d)\n", + count, skb_i, skb_i->next, skb_i->prev, + obj_state, skbuff_debugobj_state_name(skb_i), + sum_i, sum_now); + } + skb_i = skb_i->next; + count++; + } while (skb_list != skb_i); + } + pr_emerg("skb_debug: end skb list '%s'. In total %d skbs iterated.\n", list_title, count); +} + +void skbuff_debugobj_register_callback(void) +{ + skb_recycler_notifier_register(&skbuff_debug_notify); +} + +int skbuff_debug_event_handler(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct sk_buff *skb = (struct sk_buff *)data; + + pr_emerg("skb_debug: notifier event %lu\n", action); + skbuff_debugobj_print_skb(skb); + skb_recycler_print_all_lists(); + + return NOTIFY_DONE; +} diff --git a/target/linux/qualcommax/files/net/core/skbuff_debug.h b/target/linux/qualcommax/files/net/core/skbuff_debug.h new file mode 100644 index 00000000000000..43e37ba43b645e --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_debug.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013-2014, 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 + +#ifndef _LINUX_SKBBUFF_DEBUG_OBJECTS +#define _LINUX_SKBBUFF_DEBUG_OBJECTS + +#ifdef CONFIG_DEBUG_OBJECTS_SKBUFF +void skbuff_debugobj_init_and_activate(struct sk_buff *skb); +void skbuff_debugobj_activate(struct sk_buff *skb); +void skbuff_debugobj_deactivate(struct sk_buff *skb); +void skbuff_debugobj_destroy(struct sk_buff *skb); +#define skbuff_debugobj_sum_validate(skb) _skbuff_debugobj_sum_validate(skb, \ + #skb, __FILE__, __LINE__, __func__) +void _skbuff_debugobj_sum_validate(struct sk_buff *skb, const char *var, + const char *src, int line, const char *fxn); +void skbuff_debugobj_sum_update(struct sk_buff *skb); +void skbuff_debugobj_print_skb(const struct sk_buff *skb); + +void skbuff_debugobj_print_skb_list(const struct sk_buff *skb_list, + const char *list_title, int cpu); +void skbuff_debugobj_register_callback(void); + +#else +static inline void skbuff_debugobj_init_and_activate(struct sk_buff *skb) { } +static inline void skbuff_debugobj_activate(struct sk_buff *skb) { } +static inline void skbuff_debugobj_deactivate(struct sk_buff *skb) { } +static inline void skbuff_debugobj_destroy(struct sk_buff *skb) { } +static inline void skbuff_debugobj_sum_validate(struct sk_buff *skb) { } +static inline void skbuff_debugobj_sum_update(struct sk_buff *skb) { } +static inline void skbuff_debugobj_print_skb(const struct sk_buff *skb) { } + +static inline void skbuff_debugobj_print_skb_list + (const struct sk_buff *skb_list, const char *list_title, int cpu) { } +static inline void skbuff_debugobj_register_callback(void) { } +#endif + +#endif /* _LINUX_SKBBUFF_DEBUG_OBJECTS */ diff --git a/target/linux/qualcommax/files/net/core/skbuff_notifier.c b/target/linux/qualcommax/files/net/core/skbuff_notifier.c new file mode 100644 index 00000000000000..8c59476db7fe38 --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_notifier.c @@ -0,0 +1,42 @@ +/* + * 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. + */ + +/* Notifier interface for the SKB Recycler */ + +#include "skbuff_notifier.h" + +static BLOCKING_NOTIFIER_HEAD(skb_recycler_notifier); + +int skb_recycler_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&skb_recycler_notifier, nb); +} +EXPORT_SYMBOL(skb_recycler_notifier_register); + +int skb_recycler_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&skb_recycler_notifier, nb); +} +EXPORT_SYMBOL(skb_recycler_notifier_unregister); + +int skb_recycler_notifier_send_event(unsigned long action, struct sk_buff *skb) +{ + int ret; + + ret = blocking_notifier_call_chain(&skb_recycler_notifier, action, skb); + + return 0; +} diff --git a/target/linux/qualcommax/files/net/core/skbuff_notifier.h b/target/linux/qualcommax/files/net/core/skbuff_notifier.h new file mode 100644 index 00000000000000..3d8bfa586fc94b --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_notifier.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef SKBUFF_NOTIFIER_H +#define SKBUFF_NOTIFIER_H + +#include +#include + +/* notifier events */ +#define SKB_RECYCLER_NOTIFIER_SUMERR 0x0001 +#define SKB_RECYCLER_NOTIFIER_DBLFREE 0x0002 +#define SKB_RECYCLER_NOTIFIER_DBLALLOC 0x0004 +#define SKB_RECYCLER_NOTIFIER_FSM 0x0008 + +#if defined(CONFIG_DEBUG_OBJECTS_SKBUFF) +int skb_recycler_notifier_register(struct notifier_block *nb); +int skb_recycler_notifier_unregister(struct notifier_block *nb); +int skb_recycler_notifier_send_event(unsigned long action, + struct sk_buff *skb); +#else +static inline int skb_recycler_notifier_register(struct notifier_block *nb) +{ + return 0; +} + +static inline int skb_recycler_notifier_unregister(struct notifier_block *nb) +{ + return 0; +} + +static inline int skb_recycler_notifier_send_event(unsigned long action, + struct sk_buff *skb) +{ + return 1; +} +#endif /* CONFIG_DEBUG_OBJECTS_SKBUFF */ + +#endif /* SKBUFF_NOTIFIER_H */ diff --git a/target/linux/qualcommax/files/net/core/skbuff_recycle.c b/target/linux/qualcommax/files/net/core/skbuff_recycle.c new file mode 100644 index 00000000000000..cbcbc46c70d0a5 --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_recycle.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2013-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 skb recycler */ + +#include "skbuff_recycle.h" +#include +#include +#include + +#include "skbuff_debug.h" + +static struct proc_dir_entry *proc_net_skbrecycler; + +static DEFINE_PER_CPU(struct sk_buff_head, recycle_list); +static int skb_recycle_max_skbs = SKB_RECYCLE_MAX_SKBS; + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU +static DEFINE_PER_CPU(struct sk_buff_head, recycle_spare_list); +static struct global_recycler glob_recycler; +static int skb_recycle_spare_max_skbs = SKB_RECYCLE_SPARE_MAX_SKBS; +#endif + +inline struct sk_buff *skb_recycler_alloc(struct net_device *dev, + unsigned int length) +{ + unsigned long flags; + struct sk_buff_head *h; + struct sk_buff *skb = NULL; + struct sk_buff *ln = NULL; + + if (unlikely(length > SKB_RECYCLE_SIZE)) + return NULL; + + h = &get_cpu_var(recycle_list); + local_irq_save(flags); + skb = skb_peek(h); + if (skb) { + ln = skb_peek_next(skb, h); + skbuff_debugobj_activate(skb); + /* Recalculate the sum for skb->next as next and prev pointers + * of skb->next will be updated in __skb_unlink + */ + skbuff_debugobj_sum_validate(ln); + __skb_unlink(skb, h); + skbuff_debugobj_sum_update(ln); + } +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + if (unlikely(!skb)) { + u8 head; + + spin_lock(&glob_recycler.lock); + /* If global recycle list is not empty, use global buffers */ + head = glob_recycler.head; + if (unlikely(head == glob_recycler.tail)) { + spin_unlock(&glob_recycler.lock); + } else { + struct sk_buff *gn = glob_recycler.pool[head].next; + struct sk_buff *gp = glob_recycler.pool[head].prev; + + /* Move SKBs from global list to CPU pool */ + skbuff_debugobj_sum_validate(gn); + skbuff_debugobj_sum_validate(gp); + skb_queue_splice_init(&glob_recycler.pool[head], h); + skbuff_debugobj_sum_update(gn); + skbuff_debugobj_sum_update(gp); + + head = (head + 1) & SKB_RECYCLE_MAX_SHARED_POOLS_MASK; + glob_recycler.head = head; + spin_unlock(&glob_recycler.lock); + /* We have refilled the CPU pool - dequeue */ + skb = skb_peek(h); + if (skb) { + /* Recalculate the sum for skb->next as next and + * prev pointers of skb->next will be updated + * in __skb_unlink + */ + ln = skb_peek_next(skb, h); + skbuff_debugobj_activate(skb); + skbuff_debugobj_sum_validate(ln); + __skb_unlink(skb, h); + skbuff_debugobj_sum_update(ln); + } + } + } +#endif + local_irq_restore(flags); + put_cpu_var(recycle_list); + + if (likely(skb)) { + struct skb_shared_info *shinfo; + + /* We're about to write a large amount to the skb to + * zero most of the structure so prefetch the start + * of the shinfo region now so it's in the D-cache + * before we start to write that. + */ + shinfo = skb_shinfo(skb); + prefetchw(shinfo); + + zero_struct(skb, offsetof(struct sk_buff, tail)); + refcount_set(&skb->users, 1); + skb->mac_header = (typeof(skb->mac_header))~0U; + skb->transport_header = (typeof(skb->transport_header))~0U; + zero_struct(shinfo, offsetof(struct skb_shared_info, dataref)); + atomic_set(&shinfo->dataref, 1); + + skb->data = skb->head + NET_SKB_PAD; + skb_reset_tail_pointer(skb); + + skb->dev = dev; + } + + return skb; +} + +inline bool skb_recycler_consume(struct sk_buff *skb) +{ + unsigned long flags; + struct sk_buff_head *h; + struct sk_buff *ln = NULL; + /* Can we recycle this skb? If not, simply return that we cannot */ + if (unlikely(!consume_skb_can_recycle(skb, SKB_RECYCLE_MIN_SIZE, + SKB_RECYCLE_MAX_SIZE))) + return false; + + /* If we can, then it will be much faster for us to recycle this one + * later than to allocate a new one from scratch. + */ + h = &get_cpu_var(recycle_list); + local_irq_save(flags); + /* Attempt to enqueue the CPU hot recycle list first */ + if (likely(skb_queue_len(h) < skb_recycle_max_skbs)) { + ln = skb_peek(h); + /* Recalculate the sum for peek of list as next and prev + * pointers of skb->next will be updated in __skb_queue_head + */ + skbuff_debugobj_sum_validate(ln); + __skb_queue_head(h, skb); + skbuff_debugobj_deactivate(skb); + skbuff_debugobj_sum_update(ln); + local_irq_restore(flags); + preempt_enable(); + return true; + } +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + h = this_cpu_ptr(&recycle_spare_list); + + /* The CPU hot recycle list was full; if the spare list is also full, + * attempt to move the spare list to the global list for other CPUs to + * use. + */ + if (unlikely(skb_queue_len(h) >= skb_recycle_spare_max_skbs)) { + u8 cur_tail, next_tail; + + spin_lock(&glob_recycler.lock); + cur_tail = glob_recycler.tail; + next_tail = (cur_tail + 1) & SKB_RECYCLE_MAX_SHARED_POOLS_MASK; + if (next_tail != glob_recycler.head) { + struct sk_buff_head *p = &glob_recycler.pool[cur_tail]; + struct sk_buff *hn = h->next, *hp = h->prev; + + /* Move SKBs from CPU pool to Global pool*/ + skbuff_debugobj_sum_validate(hp); + skbuff_debugobj_sum_validate(hn); + skb_queue_splice_init(h, p); + skbuff_debugobj_sum_update(hp); + skbuff_debugobj_sum_update(hn); + + /* Done with global list init */ + glob_recycler.tail = next_tail; + spin_unlock(&glob_recycler.lock); + + /* Recalculate the sum for peek of list as next and prev + * pointers of skb->next will be updated in + * __skb_queue_head + */ + ln = skb_peek(h); + skbuff_debugobj_sum_validate(ln); + /* We have now cleared room in the spare; + * Initialize and enqueue skb into spare + */ + __skb_queue_head(h, skb); + skbuff_debugobj_sum_update(ln); + skbuff_debugobj_deactivate(skb); + + local_irq_restore(flags); + preempt_enable(); + return true; + } + /* We still have a full spare because the global is also full */ + spin_unlock(&glob_recycler.lock); + } else { + /* We have room in the spare list; enqueue to spare list */ + ln = skb_peek(h); + /* Recalculate the sum for peek of list as next and prev + * pointers of skb->next will be updated in __skb_queue_head + */ + skbuff_debugobj_sum_validate(ln); + __skb_queue_head(h, skb); + skbuff_debugobj_deactivate(skb); + skbuff_debugobj_sum_update(ln); + local_irq_restore(flags); + preempt_enable(); + return true; + } +#endif + + local_irq_restore(flags); + preempt_enable(); + + return false; +} + +/** + * skb_recycler_consume_list_fast - free a list of skbs + * @skb_list: head of the buffer list + * + * Add the list of given SKBs to CPU list. Assumption is that these buffers + * have been allocated originally from recycler and have been transmitted through + * a controlled fast xmit path, thus removing the need for additional checks + * before recycling the buffers back to pool + */ +#ifdef CONFIG_DEBUG_OBJECTS_SKBUFF +inline bool skb_recycler_consume_list_fast(struct sk_buff_head *skb_list) +{ + struct sk_buff *skb = NULL, *next = NULL; + + skb_queue_walk_safe(skb_list, skb, next) { + if (skb) { + __skb_unlink(skb, skb_list); + skb_recycler_consume(skb); + } + } + + return true; +} +#else +inline bool skb_recycler_consume_list_fast(struct sk_buff_head *skb_list) +{ + unsigned long flags; + struct sk_buff_head *h; + + h = &get_cpu_var(recycle_list); + local_irq_save(flags); + /* Attempt to enqueue the CPU hot recycle list first */ + if (likely(skb_queue_len(h) < skb_recycle_max_skbs)) { + skb_queue_splice(skb_list,h); + local_irq_restore(flags); + preempt_enable(); + return true; + } + + local_irq_restore(flags); + preempt_enable(); + + return false; +} +#endif + +static void skb_recycler_free_skb(struct sk_buff_head *list) +{ + struct sk_buff *skb = NULL, *next = NULL; + unsigned long flags; + + spin_lock_irqsave(&list->lock, flags); + while ((skb = skb_peek(list)) != NULL) { + skbuff_debugobj_activate(skb); + next = skb->next; + __skb_unlink(skb, list); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,2,0) + skb_release_data(skb, SKB_CONSUMED, false); +#else + skb_release_data(skb); +#endif + kfree_skbmem(skb); + /* + * Update the skb->sum for next due to skb_link operation + */ + if (next) { + skbuff_debugobj_sum_update(next); + } + } + spin_unlock_irqrestore(&list->lock, flags); +} + +static int skb_cpu_callback(unsigned int ocpu) +{ + unsigned long oldcpu = (unsigned long)ocpu; + + skb_recycler_free_skb(&per_cpu(recycle_list, oldcpu)); +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + spin_lock(&glob_recycler.lock); + skb_recycler_free_skb(&per_cpu(recycle_spare_list, oldcpu)); + spin_unlock(&glob_recycler.lock); +#endif + + return NOTIFY_DONE; +} + +#ifdef CONFIG_SKB_RECYCLER_PREALLOC +static int __init skb_prealloc_init_list(void) +{ + int i; + struct sk_buff *skb; + + for (i = 0; i < SKB_RECYCLE_MAX_PREALLOC_SKBS; i++) { + skb = __alloc_skb(SKB_RECYCLE_MAX_SIZE + NET_SKB_PAD, + GFP_KERNEL, 0, NUMA_NO_NODE); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, NET_SKB_PAD); + + skb_recycler_consume(skb); + } + return 0; +} +#endif + +/* procfs: count + * Show skb counts + */ +static int proc_skb_count_show(struct seq_file *seq, void *v) +{ + int cpu; + int len; + int total; +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + unsigned int i; + unsigned long flags; +#endif + + total = 0; + + for_each_online_cpu(cpu) { + len = skb_queue_len(&per_cpu(recycle_list, cpu)); + seq_printf(seq, "recycle_list[%d]: %d\n", cpu, len); + total += len; + } + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + for_each_online_cpu(cpu) { + len = skb_queue_len(&per_cpu(recycle_spare_list, cpu)); + seq_printf(seq, "recycle_spare_list[%d]: %d\n", cpu, len); + total += len; + } + + for (i = 0; i < SKB_RECYCLE_MAX_SHARED_POOLS; i++) { + spin_lock_irqsave(&glob_recycler.lock, flags); + len = skb_queue_len(&glob_recycler.pool[i]); + spin_unlock_irqrestore(&glob_recycler.lock, flags); + seq_printf(seq, "global_list[%d]: %d\n", i, len); + total += len; + } +#endif + + seq_printf(seq, "total: %d\n", total); + return 0; +} + +static int proc_skb_count_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_skb_count_show, pde_data(inode)); +} + +static const struct proc_ops proc_skb_count_fops = { + .proc_open = proc_skb_count_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +/* procfs: flush + * Flush skbs + */ +static void skb_recycler_flush_task(struct work_struct *work) +{ + unsigned long flags; + struct sk_buff_head *h; + struct sk_buff_head tmp; + struct sk_buff *skb = NULL; + + skb_queue_head_init(&tmp); + + h = &get_cpu_var(recycle_list); + local_irq_save(flags); + skb_queue_splice_init(h, &tmp); + /* + * Update the sum for first skb present in tmp list. + * Since the skb is changed in splice init + */ + skb = skb_peek(&tmp); + skbuff_debugobj_sum_update(skb); + local_irq_restore(flags); + put_cpu_var(recycle_list); + skb_recycler_free_skb(&tmp); + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + h = &get_cpu_var(recycle_spare_list); + local_irq_save(flags); + skb_queue_splice_init(h, &tmp); + skb = skb_peek(&tmp); + skbuff_debugobj_sum_update(skb); + local_irq_restore(flags); + put_cpu_var(recycle_spare_list); + skb_recycler_free_skb(&tmp); +#endif +} + +static ssize_t proc_skb_flush_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + unsigned int i; + unsigned long flags; +#endif + schedule_on_each_cpu(&skb_recycler_flush_task); + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + spin_lock_irqsave(&glob_recycler.lock, flags); + for (i = 0; i < SKB_RECYCLE_MAX_SHARED_POOLS; i++) + skb_recycler_free_skb(&glob_recycler.pool[i]); + glob_recycler.head = 0; + glob_recycler.tail = 0; + spin_unlock_irqrestore(&glob_recycler.lock, flags); +#endif + return count; +} + +static const struct proc_ops proc_skb_flush_fops = { + .proc_write = proc_skb_flush_write, + .proc_open = simple_open, + .proc_lseek = noop_llseek, +}; + +/* procfs: max_skbs + * Show max skbs + */ +static int proc_skb_max_skbs_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%d\n", skb_recycle_max_skbs); + return 0; +} + +static int proc_skb_max_skbs_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_skb_max_skbs_show, pde_data(inode)); +} + +static ssize_t proc_skb_max_skbs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret; + int max; + char buffer[13]; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count) != 0) + return -EFAULT; + ret = kstrtoint(strstrip(buffer), 10, &max); + if (ret == 0 && max >= 0) + skb_recycle_max_skbs = max; + + return count; +} + +static const struct proc_ops proc_skb_max_skbs_fops = { + .proc_open = proc_skb_max_skbs_open, + .proc_read = seq_read, + .proc_write = proc_skb_max_skbs_write, + .proc_release = single_release, +}; + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU +/* procfs: max_spare_skbs + * Show max spare skbs + */ +static int proc_skb_max_spare_skbs_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%d\n", skb_recycle_spare_max_skbs); + return 0; +} + +static int proc_skb_max_spare_skbs_open(struct inode *inode, struct file *file) +{ + return single_open(file, + proc_skb_max_spare_skbs_show, + pde_data(inode)); +} + +static ssize_t +proc_skb_max_spare_skbs_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret; + int max; + char buffer[13]; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count) != 0) + return -EFAULT; + ret = kstrtoint(strstrip(buffer), 10, &max); + if (ret == 0 && max >= 0) + skb_recycle_spare_max_skbs = max; + + return count; +} + +static const struct proc_ops proc_skb_max_spare_skbs_fops = { + .proc_open = proc_skb_max_spare_skbs_open, + .proc_read = seq_read, + .proc_write = proc_skb_max_spare_skbs_write, + .proc_release = single_release, +}; +#endif /* CONFIG_SKB_RECYCLER_MULTI_CPU */ + +static void skb_recycler_init_procfs(void) +{ + proc_net_skbrecycler = proc_mkdir("skb_recycler", init_net.proc_net); + if (!proc_net_skbrecycler) { + pr_err("cannot create skb_recycle proc dir"); + return; + } + + if (!proc_create("count", + S_IRUGO, + proc_net_skbrecycler, + &proc_skb_count_fops)) + pr_err("cannot create proc net skb_recycle held\n"); + + if (!proc_create("flush", + S_IWUGO, + proc_net_skbrecycler, + &proc_skb_flush_fops)) + pr_err("cannot create proc net skb_recycle flush\n"); + + if (!proc_create("max_skbs", + S_IRUGO | S_IWUGO, + proc_net_skbrecycler, + &proc_skb_max_skbs_fops)) + pr_err("cannot create proc net skb_recycle max_skbs\n"); + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + if (!proc_create("max_spare_skbs", + S_IRUGO | S_IWUGO, + proc_net_skbrecycler, + &proc_skb_max_spare_skbs_fops)) + pr_err("cannot create proc net skb_recycle max_spare_skbs\n"); +#endif +} + +void __init skb_recycler_init(void) +{ + int cpu; +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + unsigned int i; +#endif + + for_each_possible_cpu(cpu) { + skb_queue_head_init(&per_cpu(recycle_list, cpu)); + } + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + for_each_possible_cpu(cpu) { + skb_queue_head_init(&per_cpu(recycle_spare_list, cpu)); + } + + spin_lock_init(&glob_recycler.lock); + + for (i = 0; i < SKB_RECYCLE_MAX_SHARED_POOLS; i++) + skb_queue_head_init(&glob_recycler.pool[i]); + glob_recycler.head = 0; + glob_recycler.tail = 0; +#endif + +#ifdef CONFIG_SKB_RECYCLER_PREALLOC + if (skb_prealloc_init_list()) + pr_err("Failed to preallocate SKBs for recycle list\n"); +#endif + cpuhp_setup_state_nocalls(CPUHP_SKB_RECYCLER_DEAD, "net/skbuff_recycler:dead:",NULL, skb_cpu_callback); + skbuff_debugobj_register_callback(); + skb_recycler_init_procfs(); +} + +void skb_recycler_print_all_lists(void) +{ + unsigned long flags; + int cpu; +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU + int i; + struct sk_buff_head *h; + + cpu = get_cpu(); + spin_lock_irqsave(&glob_recycler.lock, flags); + for (i = 0; i < SKB_RECYCLE_MAX_SHARED_POOLS; i++) + skbuff_debugobj_print_skb_list((&glob_recycler.pool[i])->next, + "Global Pool", -1); + spin_unlock_irqrestore(&glob_recycler.lock, flags); + + preempt_disable(); + local_irq_save(flags); + + h = &per_cpu(recycle_spare_list, cpu); + skbuff_debugobj_print_skb_list(h->next, "Recycle Spare", cpu); + + local_irq_restore(flags); + preempt_enable(); +#endif + + preempt_disable(); + local_irq_save(flags); + h = &per_cpu(recycle_list, cpu); + skbuff_debugobj_print_skb_list(h->next, "Recycle List", cpu); + + local_irq_restore(flags); + preempt_enable(); +} + +#ifdef SKB_FAST_RECYCLABLE_DEBUG_ENABLE +/** + * consume_skb_can_fast_recycle_debug - Debug API to flag any sanity check + * failures on a fast recycled skb + * @skb: buffer to be checked + * @min_skb_size: minimum skb size allowed + * @max_skb_size: maximum skb size allowed + * + * Returns false with warning message if any of the checks fail + */ +static inline bool consume_skb_can_fast_recycle_debug(const struct sk_buff *skb, + int min_skb_size, int max_skb_size) +{ + if (unlikely(irqs_disabled())) { + WARN(1, "skb_debug: irqs_disabled for skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY)) { + WARN(1, "skb_debug: ZEROCOPY flag set for skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_is_nonlinear(skb))) { + WARN(1, "skb_debug: non-linear skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_shinfo(skb)->frag_list)) { + WARN(1, "skb_debug: set frag_list for skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_shinfo(skb)->nr_frags)) { + WARN(1, "skb_debug: set nr_frags for skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb->fclone != SKB_FCLONE_UNAVAILABLE)) { + WARN(1, "skb_debug: FCLONE available for skb = 0x%p \n", skb); + return false; + } + min_skb_size = SKB_DATA_ALIGN(min_skb_size + NET_SKB_PAD); + if (unlikely(skb_end_pointer(skb) - skb->head < min_skb_size)) { + WARN(1, "skb_debug: invalid min size for skb = 0x%p \n", skb); + return false; + } + max_skb_size = SKB_DATA_ALIGN(max_skb_size + NET_SKB_PAD); + if (unlikely(skb_end_pointer(skb) - skb->head > max_skb_size)) { + WARN(1, "skb_debug: invalid max size for skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_cloned(skb))) { + WARN(1, "skb_debug: cloned skb = 0x%p \n", skb); + return false; + } + if (unlikely(skb_pfmemalloc(skb))) { + WARN(1, "skb_debug: enabled pfmemalloc for skb = 0x%p \n", skb); + return false; + } + if (skb->_skb_refdst) { + WARN(1, "skb_debug: _skb_refdst flag enabled = 0x%p \n", skb); + return false; + } + if (skb->destructor) { + WARN(1, "skb_debug: destructor flag enabled = 0x%p \n", skb); + return false; + } + if (skb->active_extensions) { + WARN(1, "skb_debug: active_extensions flag enabled = 0x%p \n", + skb); + return false; + } +#if IS_ENABLED(CONFIG_NF_CONNTRACK) + if (skb->_nfct & NFCT_PTRMASK) { + WARN(1, "skb_debug: nfctinfo bits set for skb = 0x%p \n", skb); + return false; + } +#endif + return true; +} + +/** + * check_skb_fast_recyclable - Debug API to flag any sanity check failures + * on a fast recycled skb + * @skb: buffer to be checked + * + * Checks skb recyclability + */ +void check_skb_fast_recyclable(struct sk_buff *skb) +{ + bool check = true; + check = consume_skb_can_fast_recycle_debug(skb, SKB_RECYCLE_MIN_SIZE, SKB_RECYCLE_MAX_SIZE); + if (!check) + BUG_ON(1); +} +EXPORT_SYMBOL(check_skb_fast_recyclable); +#endif diff --git a/target/linux/qualcommax/files/net/core/skbuff_recycle.h b/target/linux/qualcommax/files/net/core/skbuff_recycle.h new file mode 100644 index 00000000000000..cbbc3ee1f0d6ee --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_recycle.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013-2017, 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. + * + */ +/* Definitions for the skb recycler functions */ + +#ifndef _LINUX_SKBUFF_RECYCLE_H +#define _LINUX_SKBUFF_RECYCLE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_NET_CLS_ACT +#include +#endif +#include +#include +#include +#include +#include +#include + +#define SKB_RECYCLE_SIZE 2304 +#define SKB_RECYCLE_MIN_SIZE SKB_RECYCLE_SIZE +#define SKB_RECYCLE_MAX_SIZE SKB_RECYCLE_SIZE +#define SKB_RECYCLE_MAX_SKBS 1024 + +#define SKB_RECYCLE_SPARE_MAX_SKBS 256 + +#ifdef CONFIG_SKB_RECYCLER_PREALLOC +#define SKB_RECYCLE_MAX_PREALLOC_SKBS CONFIG_SKB_RECYCLE_MAX_PREALLOC_SKBS +#define SKB_RECYCLE_MAX_SHARED_POOLS \ + DIV_ROUND_UP(SKB_RECYCLE_MAX_PREALLOC_SKBS, \ + SKB_RECYCLE_SPARE_MAX_SKBS) +#else +#define SKB_RECYCLE_MAX_SHARED_POOLS 8 +#endif + +#define SKB_RECYCLE_MAX_SHARED_POOLS_MASK \ + (SKB_RECYCLE_MAX_SHARED_POOLS - 1) + +#ifdef CONFIG_SKB_RECYCLER_MULTI_CPU +struct global_recycler { + /* Global circular list which holds the shared skb pools */ + struct sk_buff_head pool[SKB_RECYCLE_MAX_SHARED_POOLS]; + u8 head; /* head of the circular list */ + u8 tail; /* tail of the circular list */ + spinlock_t lock; +}; +#endif + +static __always_inline void zero_struct(void *v, int size) +{ + u32 *s = (u32 *)v; + + /* We assume that size is word aligned; in fact, it's constant */ + WARN_ON((size & 3) != 0); + + /* This looks odd but we "know" size is a constant, and so the + * compiler can fold away all of the conditionals. The compiler is + * pretty smart here, and can fold away the loop, too! + */ + while (size > 0) { + if (size >= 4) + s[0] = 0; + if (size >= 8) + s[1] = 0; + if (size >= 12) + s[2] = 0; + if (size >= 16) + s[3] = 0; + if (size >= 20) + s[4] = 0; + if (size >= 24) + s[5] = 0; + if (size >= 28) + s[6] = 0; + if (size >= 32) + s[7] = 0; + if (size >= 36) + s[8] = 0; + if (size >= 40) + s[9] = 0; + if (size >= 44) + s[10] = 0; + if (size >= 48) + s[11] = 0; + if (size >= 52) + s[12] = 0; + if (size >= 56) + s[13] = 0; + if (size >= 60) + s[14] = 0; + if (size >= 64) + s[15] = 0; + size -= 64; + s += 16; + } +} + +static inline bool consume_skb_can_recycle(const struct sk_buff *skb, + int min_skb_size, int max_skb_size) +{ + if (unlikely(irqs_disabled())) + return false; + + if (unlikely(skb_shinfo(skb)->tx_flags & SKBFL_ZEROCOPY_ENABLE)) + return false; + + if (unlikely(skb->head_frag)) + return false; + + if (unlikely(skb_is_nonlinear(skb))) + return false; + + if (unlikely(skb_shinfo(skb)->frag_list)) + return false; + + if (unlikely(skb_shinfo(skb)->nr_frags)) + return false; + + if (unlikely(skb->fclone != SKB_FCLONE_UNAVAILABLE)) + return false; + + min_skb_size = SKB_DATA_ALIGN(min_skb_size + NET_SKB_PAD); + if (unlikely(skb_end_pointer(skb) - skb->head < min_skb_size)) + return false; + + max_skb_size = SKB_DATA_ALIGN(max_skb_size + NET_SKB_PAD); + if (unlikely(skb_end_pointer(skb) - skb->head > max_skb_size)) + return false; + + if (unlikely(skb_cloned(skb))) + return false; + + if (unlikely(skb_pfmemalloc(skb))) + return false; + + return true; +} + +#ifdef CONFIG_SKB_RECYCLER +void __init skb_recycler_init(void); +struct sk_buff *skb_recycler_alloc(struct net_device *dev, unsigned int length); +bool skb_recycler_consume(struct sk_buff *skb); +bool skb_recycler_consume_list_fast(struct sk_buff_head *skb_list); +void skb_recycler_print_all_lists(void); +#else +#define skb_recycler_init() {} +#define skb_recycler_alloc(dev, len) NULL +#define skb_recycler_consume(skb) false +#define skb_recycler_consume_list_fast(skb_list) false +#define skb_recycler_print_all_lists() false +#endif +#endif diff --git a/target/linux/qualcommax/files/net/netfilter/nf_conntrack_dscpremark_ext.c b/target/linux/qualcommax/files/net/netfilter/nf_conntrack_dscpremark_ext.c new file mode 100644 index 00000000000000..678d27ac9df9c2 --- /dev/null +++ b/target/linux/qualcommax/files/net/netfilter/nf_conntrack_dscpremark_ext.c @@ -0,0 +1,61 @@ +/* + ************************************************************************** + * Copyright (c) 2014-2015, 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. + ************************************************************************** + */ + +/* 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); diff --git a/target/linux/qualcommax/ipq807x/base-files/usr/libexec/platform/packet-steering.sh b/target/linux/qualcommax/ipq807x/base-files/usr/libexec/platform/packet-steering.sh new file mode 100755 index 00000000000000..77e49c1cfadeb3 --- /dev/null +++ b/target/linux/qualcommax/ipq807x/base-files/usr/libexec/platform/packet-steering.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +packet_steering="$(uci -q get network.@globals[0].packet_steering)" +flow_offloading="$(uci -q get firewall.@defaults[0].flow_offloading)" +flow_offloading_hw="$(uci -q get firewall.@defaults[0].flow_offloading_hw)" + +[ "$packet_steering" = 1 ] && { + if [ ${flow_offloading_hw:-0} -gt 0 ]; then + # HW offloading + # Not implemented + : + elif [ ${flow_offloading:-0} -gt 0 ]; then + # SW offloading + : + else + # Default + # LAN + for q in $(ls /sys/class/net/lan*/queues/rx-*/rps_cpus); do echo f > $q; done + for q in $(ls /sys/class/net/lan*/queues/tx-*/xps_cpus); do echo f > $q; done + for q in $(ls /sys/class/net/lan*/queues/rx-*/rps_flow_cnt); do echo 4096 > $q; done + # WAN + for q in $(ls /sys/class/net/wan/queues/rx-*/rps_cpus); do echo f > $q; done + for q in $(ls /sys/class/net/wan/queues/tx-*/xps_cpus); do echo f > $q; done + for q in $(ls /sys/class/net/wan/queues/rx-*/rps_flow_cnt); do echo 4096 > $q; done + + echo 32768 > /proc/sys/net/core/rps_sock_flow_entries + fi +} + +# Enable threaded network backlog processing +echo 1 > /proc/sys/net/core/backlog_threaded diff --git a/target/linux/qualcommax/ipq807x/config-default b/target/linux/qualcommax/ipq807x/config-default index 18483d05b449a2..eeb86e850f04f8 100644 --- a/target/linux/qualcommax/ipq807x/config-default +++ b/target/linux/qualcommax/ipq807x/config-default @@ -1,5 +1,17 @@ CONFIG_ARM_PSCI_CPUIDLE_DOMAIN=y +CONFIG_ARM_QCOM_CPUFREQ_HW=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_DT_IDLE_GENPD=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_HZ=1000 +# CONFIG_HZ_100 is not set +CONFIG_HZ_1000=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_MASQUERADE=y CONFIG_IPQ_GCC_8074=y # CONFIG_MFD_HI6421_SPMI is not set CONFIG_MFD_SPMI_PMIC=y @@ -12,6 +24,7 @@ CONFIG_PM_GENERIC_DOMAINS_OF=y # CONFIG_POWER_RESET_QCOM_PON is not set CONFIG_QCOM_APM=y # CONFIG_QCOM_COINCELL is not set +CONFIG_QCOM_CPR=y CONFIG_QCOM_GDSC=y CONFIG_QCOM_SPMI_ADC5=y # CONFIG_QCOM_SPMI_RRADC is not set @@ -24,6 +37,11 @@ CONFIG_REGULATOR_CPR4_APSS=y CONFIG_REGULATOR_QCOM_SPMI=y # CONFIG_REGULATOR_QCOM_USB_VBUS is not set CONFIG_RTC_DRV_PM8XXX=y +# CONFIG_ALLOC_SKB_PAGE_FRAG_DISABLE is not set +# CONFIG_DEBUG_OBJECTS_SKBUFF is not set +CONFIG_SKB_RECYCLER=y +CONFIG_SKB_RECYCLER_MULTI_CPU=y +# CONFIG_SKB_FAST_RECYCLABLE_DEBUG_ENABLE is not set CONFIG_SPMI=y # CONFIG_SPMI_HISI3670 is not set CONFIG_SPMI_MSM_PMIC_ARB=y diff --git a/target/linux/qualcommax/patches-6.1/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch b/target/linux/qualcommax/patches-6.1/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch new file mode 100644 index 00000000000000..85430f888cb4bd --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch @@ -0,0 +1,311 @@ +From 6504bc9edeb1a2a54d813f4bb5d0267e7bf827f9 Mon Sep 17 00:00:00 2001 +From: Praveenkumar I +Date: Thu, 6 Feb 2020 17:35:42 +0530 +Subject: [PATCH 4/8] clk: ipq8074: Support added for necessary clocks and + reset + +Change-Id: I21a76a44185f766e9b6dcba274392ea8e599718b +Signed-off-by: Praveenkumar I +Signed-off-by: Rajkumar Ayyasamy +--- + drivers/clk/qcom/gcc-ipq8074.c | 238 ++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 35 ++- + 2 files changed, 258 insertions(+), 15 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -49,6 +49,22 @@ enum { + P_UNIPHY2_TX, + }; + ++static const char * const gcc_xo_gpll4_gpll0_gpll6_gpll0_div2[] = { ++ "xo", ++ "gpll4", ++ "gpll0", ++ "gpll6", ++ "gpll0_out_main_div2", ++}; ++ ++static const struct parent_map gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL4, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL6, 3 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_alpha_pll gpll0_main = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], +@@ -630,6 +646,12 @@ static const struct freq_tbl ftbl_pcie_a + { } + }; + ++struct freq_tbl ftbl_pcie_rchng_clk_src[] = { ++ F(19200000, P_XO, 1, 0, 0), ++ F(100000000, P_GPLL0, 8, 0, 0), ++ { } ++}; ++ + static struct clk_rcg2 pcie0_axi_clk_src = { + .cmd_rcgr = 0x75054, + .freq_tbl = ftbl_pcie_axi_clk_src, +@@ -2030,6 +2052,78 @@ static struct clk_rcg2 gp3_clk_src = { + }, + }; + ++struct freq_tbl ftbl_qdss_tsctr_clk_src[] = { ++ F(160000000, P_GPLL0_DIV2, 2.5, 0, 0), ++ F(320000000, P_GPLL0, 2.5, 0, 0), ++ F(600000000, P_GPLL6, 2, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 qdss_tsctr_clk_src = { ++ .cmd_rcgr = 0x29064, ++ .freq_tbl = ftbl_qdss_tsctr_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "qdss_tsctr_clk_src", ++ .parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2, ++ .num_parents = 5, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ ++static struct clk_fixed_factor qdss_dap_sync_clk_src = { ++ .mult = 1, ++ .div = 4, ++ .hw.init = &(struct clk_init_data){ ++ .name = "qdss_dap_sync_clk_src", ++ .parent_names = (const char *[]){ ++ "qdss_tsctr_clk_src" ++ }, ++ .num_parents = 1, ++ .ops = &clk_fixed_factor_ops, ++ }, ++}; ++ ++struct freq_tbl ftbl_qdss_at_clk_src[] = { ++ F(66670000, P_GPLL0_DIV2, 6, 0, 0), ++ F(240000000, P_GPLL6, 6, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 qdss_at_clk_src = { ++ .cmd_rcgr = 0x2900c, ++ .freq_tbl = ftbl_qdss_at_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "qdss_at_clk_src", ++ .parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2, ++ .num_parents = 5, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ ++ ++struct freq_tbl ftbl_adss_pwm_clk_src[] = { ++ F(19200000, P_XO, 1, 0, 0), ++ F(200000000, P_GPLL0, 4, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 adss_pwm_clk_src = { ++ .cmd_rcgr = 0x1c008, ++ .freq_tbl = ftbl_adss_pwm_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll0_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "adss_pwm_clk_src", ++ .parent_data = gcc_xo_gpll0, ++ .num_parents = 2, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ + static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x01008, + .clkr = { +@@ -4225,13 +4319,7 @@ static struct clk_branch gcc_gp3_clk = { + }, + }; + +-static const struct freq_tbl ftbl_pcie_rchng_clk_src[] = { +- F(19200000, P_XO, 1, 0, 0), +- F(100000000, P_GPLL0, 8, 0, 0), +- { } +-}; +- +-static struct clk_rcg2 pcie0_rchng_clk_src = { ++struct clk_rcg2 pcie0_rchng_clk_src = { + .cmd_rcgr = 0x75070, + .freq_tbl = ftbl_pcie_rchng_clk_src, + .hid_width = 5, +@@ -4323,6 +4411,114 @@ static const struct alpha_pll_config nss + .alpha_en_mask = BIT(24), + }; + ++static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = { ++ .halt_reg = 0x4700c, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x4700c, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_snoc_bus_timeout2_ahb_clk", ++ .parent_names = (const char *[]){ ++ "usb0_master_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = { ++ .halt_reg = 0x47014, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x47014, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_snoc_bus_timeout3_ahb_clk", ++ .parent_names = (const char *[]){ ++ "usb1_master_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_dcc_clk = { ++ .halt_reg = 0x77004, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x77004, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_dcc_clk", ++ .parent_names = (const char *[]){ ++ "pcnoc_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_qdss_at_clk = { ++ .halt_reg = 0x29024, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x29024, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_qdss_at_clk", ++ .parent_names = (const char *[]){ ++ "qdss_at_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_qdss_dap_clk = { ++ .halt_reg = 0x29084, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x29084, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_qdss_dap_clk", ++ .parent_names = (const char *[]){ ++ "qdss_dap_sync_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_adss_pwm_clk = { ++ .halt_reg = 0x1c020, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x1c020, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_adss_pwm_clk", ++ .parent_names = (const char *[]){ ++ "adss_pwm_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ + static struct clk_hw *gcc_ipq8074_hws[] = { + &gpll0_out_main_div2.hw, + &gpll6_out_main_div2.hw, +@@ -4331,6 +4527,7 @@ static struct clk_hw *gcc_ipq8074_hws[] + &gcc_xo_div4_clk_src.hw, + &nss_noc_clk_src.hw, + &nss_ppe_cdiv_clk_src.hw, ++ &qdss_dap_sync_clk_src.hw, + }; + + static struct clk_regmap *gcc_ipq8074_clks[] = { +@@ -4562,6 +4759,15 @@ static struct clk_regmap *gcc_ipq8074_cl + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_CRYPTO_PPE_CLK] = &gcc_crypto_ppe_clk.clkr, ++ [GCC_SNOC_BUS_TIMEOUT2_AHB_CLK] = &gcc_snoc_bus_timeout2_ahb_clk.clkr, ++ [GCC_SNOC_BUS_TIMEOUT3_AHB_CLK] = &gcc_snoc_bus_timeout3_ahb_clk.clkr, ++ [GCC_DCC_CLK] = &gcc_dcc_clk.clkr, ++ [QDSS_TSCTR_CLK_SRC] = &qdss_tsctr_clk_src.clkr, ++ [QDSS_AT_CLK_SRC] = &qdss_at_clk_src.clkr, ++ [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr, ++ [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr, ++ [ADSS_PWM_CLK_SRC] = &adss_pwm_clk_src.clkr, ++ [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr, + }; + + static const struct qcom_reset_map gcc_ipq8074_resets[] = { +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -230,10 +230,19 @@ + #define GCC_GP1_CLK 221 + #define GCC_GP2_CLK 222 + #define GCC_GP3_CLK 223 +-#define GCC_PCIE0_AXI_S_BRIDGE_CLK 224 +-#define GCC_PCIE0_RCHNG_CLK_SRC 225 +-#define GCC_PCIE0_RCHNG_CLK 226 +-#define GCC_CRYPTO_PPE_CLK 227 ++#define GCC_CRYPTO_PPE_CLK 224 ++#define GCC_PCIE0_RCHNG_CLK_SRC 225 ++#define GCC_PCIE0_RCHNG_CLK 226 ++#define GCC_PCIE0_AXI_S_BRIDGE_CLK 227 ++#define GCC_SNOC_BUS_TIMEOUT2_AHB_CLK 228 ++#define GCC_SNOC_BUS_TIMEOUT3_AHB_CLK 229 ++#define GCC_DCC_CLK 230 ++#define ADSS_PWM_CLK_SRC 231 ++#define GCC_ADSS_PWM_CLK 232 ++#define QDSS_TSCTR_CLK_SRC 233 ++#define QDSS_AT_CLK_SRC 234 ++#define GCC_QDSS_AT_CLK 235 ++#define GCC_QDSS_DAP_CLK 236 + + #define GCC_BLSP1_BCR 0 + #define GCC_BLSP1_QUP1_BCR 1 diff --git a/target/linux/qualcommax/patches-6.1/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch b/target/linux/qualcommax/patches-6.1/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch new file mode 100644 index 00000000000000..7aae767ee3ac2d --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch @@ -0,0 +1,44 @@ +From 462aa0c53397ec5bf78e3e7f68aa8a3ca300f4ba Mon Sep 17 00:00:00 2001 +From: Selvam Sathappan Periakaruppan +Date: Tue, 24 Mar 2020 19:09:38 +0530 +Subject: [PATCH 5/8] clk: qcom: ipq8074: Fix gcc_snoc_bus_timeout_ahb_clk + offset + +By default, the ipq8074 V2 clks are provided in the gcc driver. +Updating the gcc_snoc_bus_timeout_ahb_clk offsets also as needed +in ipq8074 V2. + +Change-Id: I5a6e98d002f5c3354a804e55dd9ebb1f83f7f974 +Signed-off-by: Selvam Sathappan Periakaruppan +--- + drivers/clk/qcom/gcc-ipq8074.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4412,10 +4412,10 @@ static const struct alpha_pll_config nss + }; + + static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = { +- .halt_reg = 0x4700c, ++ .halt_reg = 0x47014, + .halt_bit = 31, + .clkr = { +- .enable_reg = 0x4700c, ++ .enable_reg = 0x47014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_snoc_bus_timeout2_ahb_clk", +@@ -4430,10 +4430,10 @@ static struct clk_branch gcc_snoc_bus_ti + }; + + static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = { +- .halt_reg = 0x47014, ++ .halt_reg = 0x4701C, + .halt_bit = 31, + .clkr = { +- .enable_reg = 0x47014, ++ .enable_reg = 0x4701C, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_snoc_bus_timeout3_ahb_clk", diff --git a/target/linux/qualcommax/patches-6.1/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch b/target/linux/qualcommax/patches-6.1/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch new file mode 100644 index 00000000000000..3c7be347815cdb --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch @@ -0,0 +1,41 @@ +From 52315bec6ed633b6a71f28b746029602f8bd70b9 Mon Sep 17 00:00:00 2001 +From: Balaji Prakash J +Date: Wed, 22 Apr 2020 20:35:30 +0530 +Subject: [PATCH] clk: ipq8074: fix gcc_blsp1_ahb_clk properties + +All the voting enabled clocks does not support the enable +from CBCR register. So, updated gcc_blsp1_ahb_clk enable +register and mask to enable bit in APCS_CLOCK_BRANCH_ENA_VOTE. + +Also, the voting controlled clocks are shared among multiple +components like APSS, RPM, NSS, TZ, etc. So, turning the +voting off from APSS does not make the clock off if it has +been voted from another component. Added the flag +BRANCH_HALT_VOTED in order to skip checking the clock +disable status. + +This change is referred from the below commits, +1. 246b4fb3af9bd65d8af794aac2f0e7b1ed9cc2dd +2. c8374157d5ae91d3b3e0d513d62808a798b32d3a + +Signed-off-by: Balaji Prakash J +Change-Id: I505cb560b31ad27a02c165fbe13bb33a2fc7d230 +--- + drivers/clk/qcom/gcc-ipq8074.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -2126,9 +2126,10 @@ struct clk_rcg2 adss_pwm_clk_src = { + + static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x01008, ++ .halt_check = BRANCH_HALT_VOTED, + .clkr = { +- .enable_reg = 0x01008, +- .enable_mask = BIT(0), ++ .enable_reg = 0x0b004, ++ .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", + .parent_hws = (const struct clk_hw *[]){ diff --git a/target/linux/qualcommax/patches-6.1/0600-1-qca-nss-ecm-support-CORE.patch b/target/linux/qualcommax/patches-6.1/0600-1-qca-nss-ecm-support-CORE.patch new file mode 100644 index 00000000000000..a1cd66c4db6f5b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-1-qca-nss-ecm-support-CORE.patch @@ -0,0 +1,878 @@ +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -69,6 +69,9 @@ void brioctl_set(int (*hook)(struct net + void __user *uarg)); + int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, + struct ifreq *ifr, void __user *uarg); ++extern void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats); ++extern bool br_is_hairpin_enabled(struct net_device *dev); + + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) + int br_multicast_list_adjacent(struct net_device *dev, +@@ -211,4 +214,42 @@ static inline clock_t br_get_ageing_time + } + #endif + ++/* QCA NSS ECM support - Start */ ++extern struct net_device *br_port_dev_get(struct net_device *dev, ++ unsigned char *addr, ++ struct sk_buff *skb, ++ unsigned int cookie); ++extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); ++extern void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid); ++extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, ++ const char *addr, ++ __u16 vid); ++extern void br_fdb_update_register_notify(struct notifier_block *nb); ++extern void br_fdb_update_unregister_notify(struct notifier_block *nb); ++ ++typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev, ++ struct sk_buff *skb, ++ unsigned char *addr, ++ unsigned int cookie); ++extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook; ++ ++#define BR_FDB_EVENT_ADD 0x01 ++#define BR_FDB_EVENT_DEL 0x02 ++ ++struct br_fdb_event { ++ struct net_device *dev; ++ unsigned char addr[6]; ++ unsigned char is_local; ++ struct net_bridge *br; ++ struct net_device *orig_dev; ++}; ++extern void br_fdb_register_notify(struct notifier_block *nb); ++extern void br_fdb_unregister_notify(struct notifier_block *nb); ++ ++typedef struct net_bridge_port *br_get_dst_hook_t( ++ const struct net_bridge_port *src, ++ struct sk_buff **skb); ++extern br_get_dst_hook_t __rcu *br_get_dst_hook; ++/* QCA NSS ECM support - End */ ++ + #endif +--- a/include/linux/if_vlan.h ++++ b/include/linux/if_vlan.h +@@ -143,7 +143,10 @@ extern struct net_device *__vlan_find_de + extern int vlan_for_each(struct net_device *dev, + int (*action)(struct net_device *dev, int vid, + void *arg), void *arg); ++extern void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats); /* QCA NSS ECM support */ + extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); ++extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); /* QCA NSS ECM support */ + extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); + +@@ -236,6 +239,12 @@ extern void vlan_vids_del_by_dev(struct + extern bool vlan_uses_dev(const struct net_device *dev); + + #else ++static inline void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats) ++{ ++ ++} /* QCA NSS ECM support - End */ ++ + static inline struct net_device * + __vlan_find_dev_deep_rcu(struct net_device *real_dev, + __be16 vlan_proto, u16 vlan_id) +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2855,6 +2855,10 @@ enum netdev_cmd { + NETDEV_OFFLOAD_XSTATS_DISABLE, + NETDEV_OFFLOAD_XSTATS_REPORT_USED, + NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, ++ /* QCA NSS ECM Support - Start */ ++ NETDEV_BR_JOIN, ++ NETDEV_BR_LEAVE, ++ /* QCA NSS ECM Support - End */ + }; + const char *netdev_cmd_to_name(enum netdev_cmd cmd); + +--- a/include/net/ip6_route.h ++++ b/include/net/ip6_route.h +@@ -211,6 +211,11 @@ void rt6_multipath_rebalance(struct fib6 + void rt6_uncached_list_add(struct rt6_info *rt); + void rt6_uncached_list_del(struct rt6_info *rt); + ++/* QCA NSS ECM support - Start */ ++int rt6_register_notifier(struct notifier_block *nb); ++int rt6_unregister_notifier(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) + { + const struct dst_entry *dst = skb_dst(skb); +--- a/include/net/neighbour.h ++++ b/include/net/neighbour.h +@@ -249,6 +249,13 @@ static inline int neigh_parms_family(str + return p->tbl->family; + } + ++/* QCA NSS ECM support - Start */ ++struct neigh_mac_update { ++ unsigned char old_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; ++ unsigned char update_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; ++}; ++/* QCA NSS ECM support - End */ ++ + #define NEIGH_PRIV_ALIGN sizeof(long long) + #define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) + +@@ -397,6 +404,11 @@ int neigh_xmit(int fam, struct net_devic + void pneigh_for_each(struct neigh_table *tbl, + void (*cb)(struct pneigh_entry *)); + ++/* QCA NSS ECM support - Start */ ++extern void neigh_mac_update_register_notify(struct notifier_block *nb); ++extern void neigh_mac_update_unregister_notify(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + struct neigh_seq_state { + struct seq_net_private p; + struct neigh_table *tbl; +@@ -602,4 +614,5 @@ static inline void neigh_update_is_route + *notify = 1; + } + } ++ + #endif +--- a/include/net/route.h ++++ b/include/net/route.h +@@ -240,6 +240,11 @@ struct rtable *rt_dst_alloc(struct net_d + unsigned int flags, u16 type, bool noxfrm); + struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt); + ++/* QCA NSS ECM support - Start */ ++int ip_rt_register_notifier(struct notifier_block *nb); ++int ip_rt_unregister_notifier(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + struct in_ifaddr; + void fib_add_ifaddr(struct in_ifaddr *); + void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -2172,4 +2172,9 @@ void br_do_proxy_suppress_arp(struct sk_ + void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, + u16 vid, struct net_bridge_port *p, struct nd_msg *msg); + struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m); ++ ++/* QCA NSS ECM support - Start */ ++#define __br_get(__hook, __default, __args ...) \ ++ (__hook ? (__hook(__args)) : (__default)) ++/* QCA NSS ECM support - End */ + #endif +--- a/net/8021q/vlan_core.c ++++ b/net/8021q/vlan_core.c +@@ -72,6 +72,28 @@ bool vlan_do_receive(struct sk_buff **sk + return true; + } + ++/* QCA NSS ECM support - Start */ ++/* Update the VLAN device with statistics from network offload engines */ ++void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct vlan_pcpu_stats *stats; ++ ++ if (!is_vlan_dev(dev)) ++ return; ++ ++ stats = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, 0); ++ ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&stats->syncp); ++} ++EXPORT_SYMBOL(__vlan_dev_update_accel_stats); ++/* QCA NSS ECM support - End */ ++ + /* Must be invoked with rcu_read_lock. */ + struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, + __be16 vlan_proto, u16 vlan_id) +@@ -110,6 +132,15 @@ struct net_device *vlan_dev_real_dev(con + } + EXPORT_SYMBOL(vlan_dev_real_dev); + ++/* QCA NSS ECM support - Start */ ++/* Caller is responsible to hold the reference of the returned device */ ++struct net_device *vlan_dev_next_dev(const struct net_device *dev) ++{ ++ return vlan_dev_priv(dev)->real_dev; ++} ++EXPORT_SYMBOL(vlan_dev_next_dev); ++/* QCA NSS ECM support - End */ ++ + u16 vlan_dev_vlan_id(const struct net_device *dev) + { + return vlan_dev_priv(dev)->vlan_id; +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -33,6 +33,20 @@ static const struct rhashtable_params br + + static struct kmem_cache *br_fdb_cache __read_mostly; + ++ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list); ++ ++void br_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_register_notify); ++ ++void br_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_unregister_notify); ++ + int __init br_fdb_init(void) + { + br_fdb_cache = kmem_cache_create("bridge_fdb_cache", +@@ -188,6 +202,25 @@ static void fdb_notify(struct net_bridge + if (swdev_notify) + br_switchdev_fdb_notify(br, fdb, type); + ++ /* QCA NSS ECM support - Start */ ++ if (fdb->dst) { ++ int event; ++ struct br_fdb_event fdb_event; ++ ++ if (type == RTM_NEWNEIGH) ++ event = BR_FDB_EVENT_ADD; ++ else ++ event = BR_FDB_EVENT_DEL; ++ ++ fdb_event.dev = fdb->dst->dev; ++ ether_addr_copy(fdb_event.addr, fdb->key.addr.addr); ++ fdb_event.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); ++ atomic_notifier_call_chain(&br_fdb_notifier_list, ++ event, ++ (void *)&fdb_event); ++ } ++ /* QCA NSS ECM support - End */ ++ + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) + goto errout; +@@ -512,6 +545,22 @@ out: + spin_unlock_bh(&br->hash_lock); + } + ++/* QCA NSS ECM support - Start */ ++ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list); ++ ++void br_fdb_update_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_register_notify); ++ ++void br_fdb_update_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify); ++/* QCA NSS ECM support - End */ ++ + void br_fdb_cleanup(struct work_struct *work) + { + struct net_bridge *br = container_of(work, struct net_bridge, +@@ -520,6 +569,7 @@ void br_fdb_cleanup(struct work_struct * + unsigned long delay = hold_time(br); + unsigned long work_delay = delay; + unsigned long now = jiffies; ++ u8 mac_addr[6]; /* QCA NSS ECM support */ + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -546,8 +596,15 @@ void br_fdb_cleanup(struct work_struct * + work_delay = min(work_delay, this_timer - now); + } else { + spin_lock_bh(&br->hash_lock); +- if (!hlist_unhashed(&f->fdb_node)) ++ if (!hlist_unhashed(&f->fdb_node)) { ++ ether_addr_copy(mac_addr, f->key.addr.addr); + fdb_delete(br, f, true); ++ /* QCA NSS ECM support - Start */ ++ atomic_notifier_call_chain( ++ &br_fdb_update_notifier_list, 0, ++ (void *)mac_addr); ++ /* QCA NSS ECM support - End */ ++ } + spin_unlock_bh(&br->hash_lock); + } + } +@@ -879,6 +936,12 @@ void br_fdb_update(struct net_bridge *br + &fdb->flags))) + clear_bit(BR_FDB_ADDED_BY_EXT_LEARN, + &fdb->flags); ++ ++ /* QCA NSS ECM support - Start */ ++ atomic_notifier_call_chain( ++ &br_fdb_update_notifier_list, ++ 0, (void *)addr); ++ /* QCA NSS ECM support - End */ + } + + if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) +@@ -902,6 +965,64 @@ void br_fdb_update(struct net_bridge *br + } + } + ++/* QCA NSS ECM support - Start */ ++/* Refresh FDB entries for bridge packets being forwarded by offload engines */ ++void br_refresh_fdb_entry(struct net_device *dev, const char *addr) ++{ ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return; ++ ++ if (!is_valid_ether_addr(addr)) { ++ pr_info("bridge: Attempt to refresh with invalid ether address %pM\n", ++ addr); ++ return; ++ } ++ ++ rcu_read_lock(); ++ br_fdb_update(p->br, p, addr, 0, true); ++ rcu_read_unlock(); ++} ++EXPORT_SYMBOL_GPL(br_refresh_fdb_entry); ++ ++/* Update timestamp of FDB entries for bridge packets being forwarded by offload engines */ ++void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid) ++{ ++ struct net_bridge_fdb_entry *fdb; ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return; ++ ++ rcu_read_lock(); ++ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); ++ if (likely(fdb)) { ++ fdb->updated = jiffies; ++ } ++ rcu_read_unlock(); ++} ++EXPORT_SYMBOL_GPL(br_fdb_entry_refresh); ++ ++/* Look up the MAC address in the device's bridge fdb table */ ++struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, ++ const char *addr, __u16 vid) ++{ ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ struct net_bridge_fdb_entry *fdb; ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return NULL; ++ ++ rcu_read_lock(); ++ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); ++ rcu_read_unlock(); ++ ++ return fdb; ++} ++EXPORT_SYMBOL_GPL(br_fdb_has_entry); ++ ++/* QCA NSS ECM support - End */ + /* Dump information about entries, in response to GETNEIGH */ + int br_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -26,6 +26,12 @@ + + #include "br_private.h" + ++/* QCA NSS ECM support - Start */ ++/* Hook for external forwarding logic */ ++br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_port_dev_get_hook); ++/* QCA NSS ECM support - End */ ++ + /* + * Determine initial path cost based on speed. + * using recommendations from 802.1d standard +@@ -697,6 +703,8 @@ int br_add_if(struct net_bridge *br, str + + kobject_uevent(&p->kobj, KOBJ_ADD); + ++ call_netdevice_notifiers(NETDEV_BR_JOIN, dev); /* QCA NSS ECM support */ ++ + return 0; + + err6: +@@ -732,6 +740,8 @@ int br_del_if(struct net_bridge *br, str + if (!p || p->br != br) + return -EINVAL; + ++ call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); /* QCA NSS ECM support */ ++ + /* Since more than one interface can be attached to a bridge, + * there still maybe an alternate path for netconsole to use; + * therefore there is no reason for a NETDEV_RELEASE event. +@@ -775,3 +785,97 @@ bool br_port_flag_is_set(const struct ne + return p->flags & flag; + } + EXPORT_SYMBOL_GPL(br_port_flag_is_set); ++ ++/* br_port_dev_get() ++ * If a skb is provided, and the br_port_dev_get_hook_t hook exists, ++ * use that to try and determine the egress port for that skb. ++ * If not, or no egress port could be determined, use the given addr ++ * to identify the port to which it is reachable, ++ * returing a reference to the net device associated with that port. ++ * ++ * NOTE: Return NULL if given dev is not a bridge or the mac has no ++ * associated port. ++ */ ++struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, ++ struct sk_buff *skb, ++ unsigned int cookie) ++{ ++ struct net_bridge_fdb_entry *fdbe; ++ struct net_bridge *br; ++ struct net_device *netdev = NULL; ++ ++ /* Is this a bridge? */ ++ if (!(dev->priv_flags & IFF_EBRIDGE)) ++ return NULL; ++ ++ rcu_read_lock(); ++ ++ /* If the hook exists and the skb isn't NULL, try and get the port */ ++ if (skb) { ++ br_port_dev_get_hook_t *port_dev_get_hook; ++ ++ port_dev_get_hook = rcu_dereference(br_port_dev_get_hook); ++ if (port_dev_get_hook) { ++ struct net_bridge_port *pdst = ++ __br_get(port_dev_get_hook, NULL, dev, skb, ++ addr, cookie); ++ if (pdst) { ++ dev_hold(pdst->dev); ++ netdev = pdst->dev; ++ goto out; ++ } ++ } ++ } ++ ++ /* Either there is no hook, or can't ++ * determine the port to use - fall back to using FDB ++ */ ++ ++ br = netdev_priv(dev); ++ ++ /* Lookup the fdb entry and get reference to the port dev */ ++ fdbe = br_fdb_find_rcu(br, addr, 0); ++ if (fdbe && fdbe->dst) { ++ netdev = fdbe->dst->dev; /* port device */ ++ dev_hold(netdev); ++ } ++out: ++ rcu_read_unlock(); ++ return netdev; ++} ++EXPORT_SYMBOL_GPL(br_port_dev_get); ++ ++/* Update bridge statistics for bridge packets processed by offload engines */ ++void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct pcpu_sw_netstats *tstats; ++ ++ /* Is this a bridge? */ ++ if (!(dev->priv_flags & IFF_EBRIDGE)) ++ return; ++ ++ tstats = this_cpu_ptr(dev->tstats); ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&tstats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&tstats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&tstats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL_GPL(br_dev_update_stats); ++ ++/* QCA NSS ECM support - Start */ ++/* API to know if hairpin feature is enabled/disabled on this bridge port */ ++bool br_is_hairpin_enabled(struct net_device *dev) ++{ ++ struct net_bridge_port *port = br_port_get_check_rcu(dev); ++ ++ if (likely(port)) ++ return port->flags & BR_HAIRPIN_MODE; ++ return false; ++} ++EXPORT_SYMBOL_GPL(br_is_hairpin_enabled); ++ ++/* QCA NSS ECM support - End */ +--- a/net/core/neighbour.c ++++ b/net/core/neighbour.c +@@ -1275,6 +1275,22 @@ static void neigh_update_hhs(struct neig + } + } + ++/* QCA NSS ECM support - Start */ ++ATOMIC_NOTIFIER_HEAD(neigh_mac_update_notifier_list); ++ ++void neigh_mac_update_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&neigh_mac_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(neigh_mac_update_register_notify); ++ ++void neigh_mac_update_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&neigh_mac_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(neigh_mac_update_unregister_notify); ++/* QCA NSS ECM support - End */ ++ + /* Generic update routine. + -- lladdr is new lladdr or NULL, if it is not supplied. + -- new is new state. +@@ -1303,6 +1319,7 @@ static int __neigh_update(struct neighbo + struct net_device *dev; + int err, notify = 0; + u8 old; ++ struct neigh_mac_update nmu; /* QCA NSS ECM support */ + + trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); + +@@ -1317,7 +1334,10 @@ static int __neigh_update(struct neighbo + new = old; + goto out; + } +- if (!(flags & NEIGH_UPDATE_F_ADMIN) && ++ ++ memset(&nmu, 0, sizeof(struct neigh_mac_update)); /* QCA NSS ECM support */ ++ ++ if (!(flags & NEIGH_UPDATE_F_ADMIN) && + (old & (NUD_NOARP | NUD_PERMANENT))) + goto out; + +@@ -1354,7 +1374,12 @@ static int __neigh_update(struct neighbo + - compare new & old + - if they are different, check override flag + */ +- if ((old & NUD_VALID) && ++ /* QCA NSS ECM update - Start */ ++ memcpy(nmu.old_mac, neigh->ha, dev->addr_len); ++ memcpy(nmu.update_mac, lladdr, dev->addr_len); ++ /* QCA NSS ECM update - End */ ++ ++ if ((old & NUD_VALID) && + !memcmp(lladdr, neigh->ha, dev->addr_len)) + lladdr = neigh->ha; + } else { +@@ -1476,8 +1501,11 @@ out: + neigh_update_gc_list(neigh); + if (managed_update) + neigh_update_managed_list(neigh); +- if (notify) ++ if (notify) { + neigh_update_notify(neigh, nlmsg_pid); ++ atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0, ++ (struct neigh_mac_update *)&nmu); /* QCA NSS ECM support */ ++ } + trace_neigh_update_done(neigh, err); + return err; + } +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -1211,6 +1211,9 @@ static bool fib_valid_key_len(u32 key, u + static void fib_remove_alias(struct trie *t, struct key_vector *tp, + struct key_vector *l, struct fib_alias *old); + ++/* Define route change notification chain. */ ++static BLOCKING_NOTIFIER_HEAD(iproute_chain); /* QCA NSS ECM support */ ++ + /* Caller must hold RTNL. */ + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) +@@ -1404,6 +1407,9 @@ int fib_table_insert(struct net *net, st + rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, + &cfg->fc_nlinfo, nlflags); + succeeded: ++ blocking_notifier_call_chain(&iproute_chain, ++ RTM_NEWROUTE, fi); ++ + return 0; + + out_remove_new_fa: +@@ -1775,6 +1781,9 @@ int fib_table_delete(struct net *net, st + if (fa_to_delete->fa_state & FA_S_ACCESSED) + rt_cache_flush(cfg->fc_nlinfo.nl_net); + ++ blocking_notifier_call_chain(&iproute_chain, ++ RTM_DELROUTE, fa_to_delete->fa_info); ++ + fib_release_info(fa_to_delete->fa_info); + alias_free_mem_rcu(fa_to_delete); + return 0; +@@ -2407,6 +2416,20 @@ void __init fib_trie_init(void) + 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); + } + ++/* QCA NSS ECM support - Start */ ++int ip_rt_register_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&iproute_chain, nb); ++} ++EXPORT_SYMBOL(ip_rt_register_notifier); ++ ++int ip_rt_unregister_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&iproute_chain, nb); ++} ++EXPORT_SYMBOL(ip_rt_unregister_notifier); ++/* QCA NSS ECM support - End */ ++ + struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) + { + struct fib_table *tb; +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -666,6 +666,7 @@ void ndisc_send_ns(struct net_device *de + if (skb) + ndisc_send_skb(skb, daddr, saddr); + } ++EXPORT_SYMBOL(ndisc_send_ns); + + void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, + const struct in6_addr *daddr) +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -197,6 +197,9 @@ static void rt6_uncached_list_flush_dev( + } + } + ++/* Define route change notification chain. */ ++ATOMIC_NOTIFIER_HEAD(ip6route_chain); /* QCA NSS ECM support */ ++ + static inline const void *choose_neigh_daddr(const struct in6_addr *p, + struct sk_buff *skb, + const void *daddr) +@@ -3865,6 +3868,10 @@ int ip6_route_add(struct fib6_config *cf + return PTR_ERR(rt); + + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_NEWROUTE, rt); ++ + fib6_info_release(rt); + + return err; +@@ -3886,6 +3893,9 @@ static int __ip6_del_rt(struct fib6_info + err = fib6_del(rt, info); + spin_unlock_bh(&table->tb6_lock); + ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_DELROUTE, rt); + out: + fib6_info_release(rt); + return err; +@@ -6339,6 +6349,20 @@ static int ip6_route_dev_notify(struct n + return NOTIFY_OK; + } + ++/* QCA NSS ECM support - Start */ ++int rt6_register_notifier(struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_register(&ip6route_chain, nb); ++} ++EXPORT_SYMBOL(rt6_register_notifier); ++ ++int rt6_unregister_notifier(struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_unregister(&ip6route_chain, nb); ++} ++EXPORT_SYMBOL(rt6_unregister_notifier); ++/* QCA NSS ECM support - End */ ++ + /* + * /proc + */ +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1639,6 +1639,7 @@ const char *netdev_cmd_to_name(enum netd + N(SVLAN_FILTER_PUSH_INFO) N(SVLAN_FILTER_DROP_INFO) + N(PRE_CHANGEADDR) N(OFFLOAD_XSTATS_ENABLE) N(OFFLOAD_XSTATS_DISABLE) + N(OFFLOAD_XSTATS_REPORT_USED) N(OFFLOAD_XSTATS_REPORT_DELTA) ++ N(BR_JOIN) N(BR_LEAVE) + } + #undef N + return "UNKNOWN_NETDEV_EVENT"; +--- a/net/ipv6/addrconf.c ++++ b/net/ipv6/addrconf.c +@@ -1002,6 +1002,7 @@ void inet6_ifa_finish_destroy(struct ine + + kfree_rcu(ifp, rcu); + } ++EXPORT_SYMBOL(inet6_ifa_finish_destroy); + + static void + ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -432,6 +432,15 @@ static inline __be32 vxlan_compute_rco(u + return vni_field; + } + ++/* ++ * vxlan_get_vni() ++ * Returns the vni corresponding to tunnel ++ */ ++static inline u32 vxlan_get_vni(struct vxlan_dev *vxlan_tun) ++{ ++ return be32_to_cpu(vxlan_tun->cfg.vni); ++} ++ + static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) + { + return vs->sock->sk->sk_family; +--- a/include/uapi/linux/in.h ++++ b/include/uapi/linux/in.h +@@ -63,6 +63,8 @@ enum { + #define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ + #define IPPROTO_BEETPH IPPROTO_BEETPH ++ IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ ++#define IPPROTO_ETHERIP IPPROTO_ETHERIP + IPPROTO_ENCAP = 98, /* Encapsulation Header */ + #define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +@@ -327,7 +329,7 @@ struct sockaddr_in { + #endif + + /* contains the htonl type stuff.. */ +-#include ++#include + + + #endif /* _UAPI_LINUX_IN_H */ +--- a/tools/include/uapi/linux/in.h ++++ b/tools/include/uapi/linux/in.h +@@ -63,6 +63,8 @@ enum { + #define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ + #define IPPROTO_BEETPH IPPROTO_BEETPH ++ IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ ++#define IPPROTO_ETHERIP IPPROTO_ETHERIP + IPPROTO_ENCAP = 98, /* Encapsulation Header */ + #define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +@@ -327,7 +329,7 @@ struct sockaddr_in { + #endif + + /* contains the htonl type stuff.. */ +-#include ++#include + + + #endif /* _UAPI_LINUX_IN_H */ +--- a/net/netfilter/nf_conntrack_ecache.c ++++ b/net/netfilter/nf_conntrack_ecache.c +@@ -266,7 +266,6 @@ void nf_conntrack_register_notifier(stru + mutex_lock(&nf_ct_ecache_mutex); + notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb, + lockdep_is_held(&nf_ct_ecache_mutex)); +- WARN_ON_ONCE(notify); + rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); + mutex_unlock(&nf_ct_ecache_mutex); + } +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -26,6 +26,7 @@ struct nf_tcp_net { + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + u8 tcp_loose; + u8 tcp_be_liberal; ++ u8 tcp_no_window_check; + u8 tcp_max_retrans; + u8 tcp_ignore_invalid_rst; + #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -513,11 +513,15 @@ tcp_in_window(struct nf_conn *ct, enum ip_conntrack_dir dir, + struct ip_ct_tcp *state = &ct->proto.tcp; + struct ip_ct_tcp_state *sender = &state->seen[dir]; + struct ip_ct_tcp_state *receiver = &state->seen[!dir]; ++ const struct nf_tcp_net *tn = nf_tcp_pernet(nf_ct_net(ct)); + __u32 seq, ack, sack, end, win, swin; + bool in_recv_win, seq_ok; + s32 receiver_offset; + u16 win_raw; + ++ if (tn->tcp_no_window_check) ++ return NFCT_TCP_ACCEPT; ++ + /* + * Get the required data from the packet. + */ +@@ -1257,7 +1261,7 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; +- else if (ct->proto.tcp.last_win == 0 && ++ else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 && + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; + else +@@ -1573,6 +1577,9 @@ void nf_conntrack_tcp_init_net(struct net *net) + */ + tn->tcp_be_liberal = 0; + ++ /* Skip Windows Check */ ++ tn->tcp_no_window_check = 0; ++ + /* If it's non-zero, we turn off RST sequence number check */ + tn->tcp_ignore_invalid_rst = 0; + +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -637,6 +637,7 @@ enum nf_ct_sysctl_index { + #endif + NF_SYSCTL_CT_PROTO_TCP_LOOSE, + NF_SYSCTL_CT_PROTO_TCP_LIBERAL, ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, + NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, + NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, +@@ -844,6 +845,14 @@ static struct ctl_table nf_ct_sysctl_table[] = { + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .maxlen = sizeof(u8), ++ .mode = 0644, ++ .proc_handler = proc_dou8vec_minmax, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE, ++ }, + [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { + .procname = "nf_conntrack_tcp_ignore_invalid_rst", + .maxlen = sizeof(u8), +@@ -1054,6 +1063,7 @@ static void nf_conntrack_standalone_init_tcp_sysctl(struct net *net, + + XASSIGN(LOOSE, &tn->tcp_loose); + XASSIGN(LIBERAL, &tn->tcp_be_liberal); ++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); + XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); + XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); + #undef XASSIGN diff --git a/target/linux/qualcommax/patches-6.1/0600-2-qca-nss-ecm-support-PPPOE-offload.patch b/target/linux/qualcommax/patches-6.1/0600-2-qca-nss-ecm-support-PPPOE-offload.patch new file mode 100644 index 00000000000000..01c13a341f7029 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-2-qca-nss-ecm-support-PPPOE-offload.patch @@ -0,0 +1,600 @@ +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -48,6 +48,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -254,6 +255,25 @@ struct ppp_net { + #define seq_before(a, b) ((s32)((a) - (b)) < 0) + #define seq_after(a, b) ((s32)((a) - (b)) > 0) + ++ ++/* ++ * Registration/Unregistration methods ++ * for PPP channel connect and disconnect event notifications. ++ */ ++RAW_NOTIFIER_HEAD(ppp_channel_connection_notifier_list); ++ ++void ppp_channel_connection_register_notify(struct notifier_block *nb) ++{ ++ raw_notifier_chain_register(&ppp_channel_connection_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(ppp_channel_connection_register_notify); ++ ++void ppp_channel_connection_unregister_notify(struct notifier_block *nb) ++{ ++ raw_notifier_chain_unregister(&ppp_channel_connection_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(ppp_channel_connection_unregister_notify); ++ + /* Prototypes. */ + static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, + struct file *file, unsigned int cmd, unsigned long arg); +@@ -3453,7 +3473,10 @@ ppp_connect_channel(struct channel *pch, + struct ppp_net *pn; + int ret = -ENXIO; + int hdrlen; ++ int ppp_proto; ++ int version; + ++ int notify = 0; + pn = ppp_pernet(pch->chan_net); + + mutex_lock(&pn->all_ppp_mutex); +@@ -3485,13 +3508,40 @@ ppp_connect_channel(struct channel *pch, + ++ppp->n_channels; + pch->ppp = ppp; + refcount_inc(&ppp->file.refcnt); ++ ++ /* Set the netdev priv flag if the prototype ++ * is L2TP or PPTP. Return success in all cases ++ */ ++ if (!pch->chan) ++ goto out2; ++ ++ ppp_proto = ppp_channel_get_protocol(pch->chan); ++ if (ppp_proto == PX_PROTO_PPTP) { ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_PPTP; ++ } else if (ppp_proto == PX_PROTO_OL2TP) { ++ version = ppp_channel_get_proto_version(pch->chan); ++ if (version == 2) ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV2; ++ else if (version == 3) ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV3; ++ } ++ notify = 1; ++ ++ out2: + ppp_unlock(ppp); + ret = 0; +- + outl: + write_unlock_bh(&pch->upl); + out: + mutex_unlock(&pn->all_ppp_mutex); ++ ++ if (notify && ppp && ppp->dev) { ++ dev_hold(ppp->dev); ++ raw_notifier_call_chain(&ppp_channel_connection_notifier_list, ++ PPP_CHANNEL_CONNECT, ppp->dev); ++ dev_put(ppp->dev); ++ } ++ + return ret; + } + +@@ -3509,6 +3559,13 @@ ppp_disconnect_channel(struct channel *p + pch->ppp = NULL; + write_unlock_bh(&pch->upl); + if (ppp) { ++ if (ppp->dev) { ++ dev_hold(ppp->dev); ++ raw_notifier_call_chain(&ppp_channel_connection_notifier_list, ++ PPP_CHANNEL_DISCONNECT, ppp->dev); ++ dev_put(ppp->dev); ++ } ++ + /* remove it from the ppp unit's list */ + ppp_lock(ppp); + list_del(&pch->clist); +@@ -3588,6 +3645,222 @@ static void *unit_find(struct idr *p, in + return idr_find(p, n); + } + ++/* Updates the PPP interface statistics. */ ++void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped) ++{ ++ struct ppp *ppp; ++ ++ if (!dev) ++ return; ++ ++ if (dev->type != ARPHRD_PPP) ++ return; ++ ++ ppp = netdev_priv(dev); ++ ++ ppp_xmit_lock(ppp); ++ ppp->stats64.tx_packets += tx_packets; ++ ppp->stats64.tx_bytes += tx_bytes; ++ ppp->dev->stats.tx_errors += tx_errors; ++ ppp->dev->stats.tx_dropped += tx_dropped; ++ if (tx_packets) ++ ppp->last_xmit = jiffies; ++ ppp_xmit_unlock(ppp); ++ ++ ppp_recv_lock(ppp); ++ ppp->stats64.rx_packets += rx_packets; ++ ppp->stats64.rx_bytes += rx_bytes; ++ ppp->dev->stats.rx_errors += rx_errors; ++ ppp->dev->stats.rx_dropped += rx_dropped; ++ if (rx_packets) ++ ppp->last_recv = jiffies; ++ ppp_recv_unlock(ppp); ++} ++ ++/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if ++ * the device is not PPP. ++ */ ++int ppp_is_multilink(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ unsigned int flags; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ppp_lock(ppp); ++ flags = ppp->flags; ++ ppp_unlock(ppp); ++ ++ if (flags & SC_MULTILINK) ++ return 1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ppp_is_multilink); ++ ++/* ppp_channel_get_protocol() ++ * Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ * ++ * NOTE: Some channels do not use PX sockets so the protocol value may be very ++ * different for them. ++ * NOTE: -1 indicates failure. ++ * NOTE: Once you know the channel protocol you may then either cast 'chan' to ++ * its sub-class or use the channel protocol specific API's as provided by that ++ * channel sub type. ++ */ ++int ppp_channel_get_protocol(struct ppp_channel *chan) ++{ ++ if (!chan->ops->get_channel_protocol) ++ return -1; ++ ++ return chan->ops->get_channel_protocol(chan); ++} ++EXPORT_SYMBOL(ppp_channel_get_protocol); ++ ++/* ppp_channel_get_proto_version() ++ * Call this to get channel protocol version ++ */ ++int ppp_channel_get_proto_version(struct ppp_channel *chan) ++{ ++ if (!chan->ops->get_channel_protocol_ver) ++ return -1; ++ ++ return chan->ops->get_channel_protocol_ver(chan); ++} ++EXPORT_SYMBOL(ppp_channel_get_proto_version); ++ ++/* ppp_channel_hold() ++ * Call this to hold a channel. ++ * ++ * Returns true on success or false if the hold could not happen. ++ * ++ * NOTE: chan must be protected against destruction during this call - ++ * either by correct locking etc. or because you already have an implicit ++ * or explicit hold to the channel already and this is an additional hold. ++ */ ++bool ppp_channel_hold(struct ppp_channel *chan) ++{ ++ if (!chan->ops->hold) ++ return false; ++ ++ chan->ops->hold(chan); ++ return true; ++} ++EXPORT_SYMBOL(ppp_channel_hold); ++ ++/* ppp_channel_release() ++ * Call this to release a hold you have upon a channel ++ */ ++void ppp_channel_release(struct ppp_channel *chan) ++{ ++ chan->ops->release(chan); ++} ++EXPORT_SYMBOL(ppp_channel_release); ++ ++/* Check if ppp xmit lock is on hold */ ++bool ppp_is_xmit_locked(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ ++ if (!dev) ++ return false; ++ ++ if (dev->type != ARPHRD_PPP) ++ return false; ++ ++ ppp = netdev_priv(dev); ++ if (!ppp) ++ return false; ++ ++ if (spin_is_locked(&(ppp)->wlock)) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL(ppp_is_xmit_locked); ++ ++/* ppp_hold_channels() ++ * Returns the PPP channels of the PPP device, storing each one into ++ * channels[]. ++ * ++ * channels[] has chan_sz elements. ++ * This function returns the number of channels stored, up to chan_sz. ++ * It will return < 0 if the device is not PPP. ++ * ++ * You MUST release the channels using ppp_release_channels(). ++ */ ++int ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], ++ unsigned int chan_sz) ++{ ++ struct ppp *ppp; ++ int c; ++ struct channel *pch; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ++ c = 0; ++ ppp_lock(ppp); ++ list_for_each_entry(pch, &ppp->channels, clist) { ++ struct ppp_channel *chan; ++ ++ if (!pch->chan) { ++ /* Channel is going / gone away */ ++ continue; ++ } ++ ++ if (c == chan_sz) { ++ /* No space to record channel */ ++ ppp_unlock(ppp); ++ return c; ++ } ++ ++ /* Hold the channel, if supported */ ++ chan = pch->chan; ++ if (!chan->ops->hold) ++ continue; ++ ++ chan->ops->hold(chan); ++ ++ /* Record the channel */ ++ channels[c++] = chan; ++ } ++ ppp_unlock(ppp); ++ return c; ++} ++EXPORT_SYMBOL(ppp_hold_channels); ++ ++/* ppp_release_channels() ++ * Releases channels ++ */ ++void ppp_release_channels(struct ppp_channel *channels[], unsigned int chan_sz) ++{ ++ unsigned int c; ++ ++ for (c = 0; c < chan_sz; ++c) { ++ struct ppp_channel *chan; ++ ++ chan = channels[c]; ++ chan->ops->release(chan); ++ } ++} ++EXPORT_SYMBOL(ppp_release_channels); ++ + /* Module/initialization stuff */ + + module_init(ppp_init); +@@ -3604,6 +3877,7 @@ EXPORT_SYMBOL(ppp_input_error); + EXPORT_SYMBOL(ppp_output_wakeup); + EXPORT_SYMBOL(ppp_register_compressor); + EXPORT_SYMBOL(ppp_unregister_compressor); ++EXPORT_SYMBOL(ppp_update_stats); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); + MODULE_ALIAS_RTNL_LINK("ppp"); +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -62,6 +62,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -87,7 +88,7 @@ + static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb); + + static const struct proto_ops pppoe_ops; +-static const struct ppp_channel_ops pppoe_chan_ops; ++static const struct pppoe_channel_ops pppoe_chan_ops; + + /* per-net private data for this module */ + static unsigned int pppoe_net_id __read_mostly; +@@ -692,7 +693,7 @@ static int pppoe_connect(struct socket * + + po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2; + po->chan.private = sk; +- po->chan.ops = &pppoe_chan_ops; ++ po->chan.ops = (struct ppp_channel_ops *)&pppoe_chan_ops; + + error = ppp_register_net_channel(dev_net(dev), &po->chan); + if (error) { +@@ -995,9 +996,80 @@ static int pppoe_fill_forward_path(struc + return 0; + } + +-static const struct ppp_channel_ops pppoe_chan_ops = { +- .start_xmit = pppoe_xmit, +- .fill_forward_path = pppoe_fill_forward_path, ++/************************************************************************ ++ * ++ * function called by generic PPP driver to hold channel ++ * ++ ***********************************************************************/ ++static void pppoe_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/************************************************************************ ++ * ++ * function called by generic PPP driver to release channel ++ * ++ ***********************************************************************/ ++static void pppoe_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/************************************************************************ ++ * ++ * function called to get the channel protocol type ++ * ++ ***********************************************************************/ ++static int pppoe_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_OE; ++} ++ ++/************************************************************************ ++ * ++ * function called to get the PPPoE channel addressing ++ * NOTE: This function returns a HOLD to the netdevice ++ * ++ ***********************************************************************/ ++static int pppoe_get_addressing(struct ppp_channel *chan, ++ struct pppoe_opt *addressing) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ struct pppox_sock *po = pppox_sk(sk); ++ int err = 0; ++ ++ *addressing = po->proto.pppoe; ++ if (!addressing->dev) ++ return -ENODEV; ++ ++ dev_hold(addressing->dev); ++ return err; ++} ++ ++/* pppoe_channel_addressing_get() ++ * Return PPPoE channel specific addressing information. ++ */ ++int pppoe_channel_addressing_get(struct ppp_channel *chan, ++ struct pppoe_opt *addressing) ++{ ++ return pppoe_get_addressing(chan, addressing); ++} ++EXPORT_SYMBOL(pppoe_channel_addressing_get); ++ ++static const struct pppoe_channel_ops pppoe_chan_ops = { ++ /* PPPoE specific channel ops */ ++ .get_addressing = pppoe_get_addressing, ++ /* General ppp channel ops */ ++ .ops.start_xmit = pppoe_xmit, ++ .ops.get_channel_protocol = pppoe_get_channel_protocol, ++ .ops.hold = pppoe_hold_chan, ++ .ops.release = pppoe_release_chan, ++ .ops.fill_forward_path = pppoe_fill_forward_path, + }; + + static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -91,4 +91,17 @@ enum { + PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ + }; + ++/* ++ * PPPoE Channel specific operations ++ */ ++struct pppoe_channel_ops { ++ /* Must be first - general to all PPP channels */ ++ struct ppp_channel_ops ops; ++ int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *); ++}; ++ ++/* Return PPPoE channel specific addressing information */ ++extern int pppoe_channel_addressing_get(struct ppp_channel *chan, ++ struct pppoe_opt *addressing); ++ + #endif /* !(__LINUX_IF_PPPOX_H) */ +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1762,6 +1762,36 @@ enum netdev_priv_flags { + IFF_NO_IP_ALIGN = BIT_ULL(34), + }; + ++/** ++ * enum netdev_priv_flags_ext - &struct net_device priv_flags_ext ++ * ++ * These flags are used to check for device type and can be ++ * set and used by the drivers ++ * ++ * @IFF_EXT_TUN_TAP: device is a TUN/TAP device ++ * @IFF_EXT_PPP_L2TPV2: device is a L2TPV2 device ++ * @IFF_EXT_PPP_L2TPV3: device is a L2TPV3 device ++ * @IFF_EXT_PPP_PPTP: device is a PPTP device ++ * @IFF_EXT_GRE_V4_TAP: device is a GRE IPv4 TAP device ++ * @IFF_EXT_GRE_V6_TAP: device is a GRE IPv6 TAP device ++ * @IFF_EXT_IFB: device is an IFB device ++ * @IFF_EXT_MAPT: device is an MAPT device ++ * @IFF_EXT_HW_NO_OFFLOAD: device is an NON Offload device ++ * @IFF_EXT_L2TPV3: device is a L2TPV3 Ethernet device ++ */ ++enum netdev_priv_flags_ext { ++ IFF_EXT_TUN_TAP = 1<<0, ++ IFF_EXT_PPP_L2TPV2 = 1<<1, ++ IFF_EXT_PPP_L2TPV3 = 1<<2, ++ IFF_EXT_PPP_PPTP = 1<<3, ++ IFF_EXT_GRE_V4_TAP = 1<<4, ++ IFF_EXT_GRE_V6_TAP = 1<<5, ++ IFF_EXT_IFB = 1<<6, ++ IFF_EXT_MAPT = 1<<7, ++ IFF_EXT_HW_NO_OFFLOAD = 1<<8, ++ IFF_EXT_ETH_L2TPV3 = 1<<9, ++}; ++ + #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN + #define IFF_EBRIDGE IFF_EBRIDGE + #define IFF_BONDING IFF_BONDING +@@ -2075,6 +2103,7 @@ struct net_device { + /* Read-mostly cache-line for fast-path access */ + unsigned int flags; + unsigned long long priv_flags; ++ unsigned int priv_flags_ext; + const struct net_device_ops *netdev_ops; + int ifindex; + unsigned short gflags; +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -19,6 +19,10 @@ + #include + #include + #include ++#include ++ ++#define PPP_CHANNEL_DISCONNECT 0 ++#define PPP_CHANNEL_CONNECT 1 + + struct net_device_path; + struct net_device_path_ctx; +@@ -30,9 +34,19 @@ struct ppp_channel_ops { + int (*start_xmit)(struct ppp_channel *, struct sk_buff *); + /* Handle an ioctl call that has come in via /dev/ppp. */ + int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long); ++ /* Get channel protocol type, one of PX_PROTO_XYZ or specific to ++ * the channel subtype ++ */ ++ int (*get_channel_protocol)(struct ppp_channel *); ++ /* Get channel protocol version */ ++ int (*get_channel_protocol_ver)(struct ppp_channel *); ++ /* Hold the channel from being destroyed */ ++ void (*hold)(struct ppp_channel *); ++ /* Release hold on the channel */ ++ void (*release)(struct ppp_channel *); + int (*fill_forward_path)(struct net_device_path_ctx *, +- struct net_device_path *, +- const struct ppp_channel *); ++ struct net_device_path *, ++ const struct ppp_channel *); + }; + + struct ppp_channel { +@@ -76,6 +90,51 @@ extern int ppp_unit_number(struct ppp_ch + /* Get the device name associated with a channel, or NULL if none */ + extern char *ppp_dev_name(struct ppp_channel *); + ++/* Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ */ ++extern int ppp_channel_get_protocol(struct ppp_channel *); ++ ++/* Call this get protocol version */ ++extern int ppp_channel_get_proto_version(struct ppp_channel *); ++ ++/* Call this to hold a channel */ ++extern bool ppp_channel_hold(struct ppp_channel *); ++ ++/* Call this to release a hold you have upon a channel */ ++extern void ppp_channel_release(struct ppp_channel *); ++ ++/* Release hold on PPP channels */ ++extern void ppp_release_channels(struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Hold PPP channels for the PPP device */ ++extern int ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if ppp xmit lock is locked */ ++extern bool ppp_is_xmit_locked(struct net_device *dev); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int ppp_is_multilink(struct net_device *dev); ++ ++/* Register the PPP channel connect notifier */ ++extern void ppp_channel_connection_register_notify(struct notifier_block *nb); ++ ++/* Unregister the PPP channel connect notifier */ ++extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb); ++ ++/* Update statistics of the PPP net_device by incrementing related ++ * statistics field value with corresponding parameter ++ */ ++extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped); ++ ++ + /* + * SMP locking notes: + * The channel code must ensure that when it calls ppp_unregister_channel, diff --git a/target/linux/qualcommax/patches-6.1/0600-3-qca-nss-ecm-support-net-bonding.patch b/target/linux/qualcommax/patches-6.1/0600-3-qca-nss-ecm-support-net-bonding.patch new file mode 100644 index 00000000000000..f2cb4f3a91fd23 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-3-qca-nss-ecm-support-net-bonding.patch @@ -0,0 +1,49 @@ +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -208,6 +208,7 @@ atomic_t netpoll_block_tx = ATOMIC_INIT( + #endif + + unsigned int bond_net_id __read_mostly; ++static unsigned long bond_id_mask = 0xFFFFFFF0; /* QCA NSS ECM bonding support */ + + static const struct flow_dissector_key flow_keys_bonding_keys[] = { + { +@@ -5793,8 +5794,14 @@ static void bond_destructor(struct net_d + if (bond->wq) + destroy_workqueue(bond->wq); + ++ /* QCA NSS ECM bonding support - Start */ ++ if (bond->id != (~0U)) ++ clear_bit(bond->id, &bond_id_mask); ++ /* QCA NSS ECM bonding support - End */ ++ + if (bond->rr_tx_counter) + free_percpu(bond->rr_tx_counter); ++ + } + + void bond_setup(struct net_device *bond_dev) +@@ -6358,6 +6365,13 @@ int bond_create(struct net *net, const c + + bond_work_init_all(bond); + ++ /* QCA NSS ECM bonding support - Start */ ++ bond->id = ~0U; ++ if (bond_id_mask != (~0UL)) { ++ bond->id = (u32)ffz(bond_id_mask); ++ set_bit(bond->id, &bond_id_mask); ++ } ++ /* QCA NSS ECM bonding support - End */ + out: + rtnl_unlock(); + return res; +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -265,6 +265,7 @@ struct bonding { + spinlock_t ipsec_lock; + #endif /* CONFIG_XFRM_OFFLOAD */ + struct bpf_prog *xdp_prog; ++ u32 id;/* QCA NSS ECM bonding support */ + }; + + #define bond_slave_get_rcu(dev) \ diff --git a/target/linux/qualcommax/patches-6.1/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch b/target/linux/qualcommax/patches-6.1/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch new file mode 100644 index 00000000000000..0548b17c8eb52a --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch @@ -0,0 +1,685 @@ +--- a/drivers/net/bonding/bond_3ad.c ++++ b/drivers/net/bonding/bond_3ad.c +@@ -115,6 +115,40 @@ static void ad_marker_response_received( + struct port *port); + static void ad_update_actor_keys(struct port *port, bool reset); + ++/* QCA NSS ECM bonding support - Start */ ++struct bond_cb __rcu *bond_cb; ++ ++int bond_register_cb(struct bond_cb *cb) ++{ ++ struct bond_cb *lag_cb; ++ ++ lag_cb = kzalloc(sizeof(*lag_cb), GFP_ATOMIC | __GFP_NOWARN); ++ if (!lag_cb) { ++ return -1; ++ } ++ ++ memcpy((void *)lag_cb, (void *)cb, sizeof(*cb)); ++ ++ rcu_read_lock(); ++ rcu_assign_pointer(bond_cb, lag_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(bond_register_cb); ++ ++void bond_unregister_cb(void) ++{ ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ rcu_assign_pointer(bond_cb, NULL); ++ rcu_read_unlock(); ++ ++ kfree(lag_cb_main); ++} ++EXPORT_SYMBOL(bond_unregister_cb); ++/* QCA NSS ECM bonding support - End */ + + /* ================= api to bonding and kernel code ================== */ + +@@ -1064,7 +1098,31 @@ static void ad_mux_machine(struct port * + ad_disable_collecting_distributing(port, + update_slave_arr); + port->ntt = true; ++ ++ /* QCA NSS ECM bonding support - Start */ ++ /* Send a notificaton about change in state of this ++ * port. We only want to handle case where port moves ++ * from AD_MUX_COLLECTING_DISTRIBUTING -> ++ * AD_MUX_ATTACHED. ++ */ ++ if (bond_slave_is_up(port->slave) && ++ (last_state == AD_MUX_COLLECTING_DISTRIBUTING)) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_down) { ++ struct net_device *dev; ++ ++ dev = port->slave->dev; ++ lag_cb_main->bond_cb_link_down(dev); ++ } ++ rcu_read_unlock(); ++ } ++ + break; ++ /* QCA NSS ECM bonding support - End */ + case AD_MUX_COLLECTING_DISTRIBUTING: + port->actor_oper_port_state |= LACP_STATE_COLLECTING; + port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; +@@ -1908,6 +1966,7 @@ static void ad_enable_collecting_distrib + bool *update_slave_arr) + { + if (port->aggregator->is_active) { ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + slave_dbg(port->slave->bond->dev, port->slave->dev, + "Enabling port %d (LAG %d)\n", + port->actor_port_number, +@@ -1915,6 +1974,16 @@ static void ad_enable_collecting_distrib + __enable_port(port); + /* Slave array needs update */ + *update_slave_arr = true; ++ ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(port->slave->dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ + } + } + +@@ -2674,6 +2743,104 @@ int bond_3ad_get_active_agg_info(struct + return ret; + } + ++/* QCA NSS ECM bonding support - Start */ ++/* bond_3ad_get_tx_dev - Calculate egress interface for a given packet, ++ * for a LAG that is configured in 802.3AD mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address ++ * @dst: pointer to destination L3 address ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash is used to calculate hash using L2/L3 ++ * addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, u8 *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct aggregator *agg; ++ struct ad_info ad_info; ++ struct list_head *iter; ++ struct slave *slave; ++ struct slave *first_ok_slave = NULL; ++ u32 hash = 0; ++ int slaves_in_agg; ++ int slave_agg_no = 0; ++ int agg_id; ++ ++ if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { ++ pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ slaves_in_agg = ad_info.ports; ++ agg_id = ad_info.aggregator_id; ++ ++ if (slaves_in_agg == 0) { ++ pr_debug("%s: Error: active aggregator is empty\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_agg_no = hash % slaves_in_agg; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for 802.3AD fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ slave_agg_no = hash % slaves_in_agg; ++ } ++ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ agg = SLAVE_AD_INFO(slave)->port.aggregator; ++ if (!agg || agg->aggregator_identifier != agg_id) ++ continue; ++ ++ if (slave_agg_no >= 0) { ++ if (!first_ok_slave && bond_slave_can_tx(slave)) ++ first_ok_slave = slave; ++ slave_agg_no--; ++ continue; ++ } ++ ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ if (slave_agg_no >= 0) { ++ pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n", ++ bond_dev->name, agg_id); ++ return NULL; ++ } ++ ++ /* we couldn't find any suitable slave after the agg_no, so use the ++ * first suitable found, if found. ++ */ ++ if (first_ok_slave) ++ return first_ok_slave->dev; ++ ++ return NULL; ++} ++/* QCA NSS ECM bonding support - End */ ++ + int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) + { +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -286,6 +286,21 @@ const char *bond_mode_name(int mode) + return names[mode]; + } + ++/* QCA NSS ECM bonding support */ ++int bond_get_id(struct net_device *bond_dev) ++{ ++ struct bonding *bond; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return -EINVAL; ++ ++ bond = netdev_priv(bond_dev); ++ return bond->id; ++} ++EXPORT_SYMBOL(bond_get_id); ++/* QCA NSS ECM bonding support */ ++ + /** + * bond_dev_queue_xmit - Prepare skb for xmit. + * +@@ -1185,6 +1200,23 @@ void bond_change_active_slave(struct bon + if (BOND_MODE(bond) == BOND_MODE_8023AD) + bond_3ad_handle_link_change(new_active, BOND_LINK_UP); + ++ /* QCA NSS ECM bonding support - Start */ ++ if (bond->params.mode == BOND_MODE_XOR) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_up) { ++ struct net_device *dev; ++ ++ dev = new_active->dev; ++ lag_cb_main->bond_cb_link_up(dev); ++ } ++ rcu_read_unlock(); ++ } ++ /* QCA NSS ECM bonding support - End */ ++ + if (bond_is_lb(bond)) + bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); + } else { +@@ -1808,6 +1840,7 @@ int bond_enslave(struct net_device *bond + const struct net_device_ops *slave_ops = slave_dev->netdev_ops; + struct slave *new_slave = NULL, *prev_slave; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + int link_reporting; + int res = 0, i; + +@@ -2251,6 +2284,15 @@ int bond_enslave(struct net_device *bond + bond_is_active_slave(new_slave) ? "an active" : "a backup", + new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + /* enslave is successful */ + bond_queue_slave_event(new_slave); + return 0; +@@ -2316,6 +2358,15 @@ err_undo_flags: + } + } + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + return res; + } + +@@ -2337,6 +2388,7 @@ static int __bond_release_one(struct net + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave, *oldcurrent; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + int old_flags = bond_dev->flags; + netdev_features_t old_features = bond_dev->features; + +@@ -2359,6 +2411,15 @@ static int __bond_release_one(struct net + + bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_release) ++ lag_cb_main->bond_cb_release(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + bond_sysfs_slave_del(slave); + + /* recompute stats just before removing the slave */ +@@ -2678,6 +2739,8 @@ static void bond_miimon_commit(struct bo + struct slave *slave, *primary, *active; + bool do_failover = false; + struct list_head *iter; ++ struct net_device *slave_dev = NULL; /* QCA NSS ECM bonding support */ ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + + ASSERT_RTNL(); + +@@ -2717,6 +2780,12 @@ static void bond_miimon_commit(struct bo + bond_set_active_slave(slave); + } + ++ /* QCA NSS ECM bonding support - Start */ ++ if ((bond->params.mode == BOND_MODE_XOR) && ++ (!slave_dev)) ++ slave_dev = slave->dev; ++ /* QCA NSS ECM bonding support - End */ ++ + slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", + slave->speed == SPEED_UNKNOWN ? 0 : slave->speed, + slave->duplex ? "full" : "half"); +@@ -2765,6 +2834,16 @@ static void bond_miimon_commit(struct bo + unblock_netpoll_tx(); + } + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (slave_dev && lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + bond_set_carrier(bond); + } + +@@ -4012,8 +4091,219 @@ static inline u32 bond_eth_hash(struct s + return 0; + + ep = (struct ethhdr *)(data + mhoff); +- return ep->h_dest[5] ^ ep->h_source[5] ^ be16_to_cpu(ep->h_proto); ++ return ep->h_dest[5] ^ ep->h_source[5]; /* QCA NSS ECM bonding support */ ++} ++ ++/* QCA NSS ECM bonding support - Start */ ++/* Extract the appropriate headers based on bond's xmit policy */ ++static bool bond_flow_dissect_without_skb(struct bonding *bond, ++ u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, ++ u16 protocol, __be16 *layer4hdr, ++ struct flow_keys *fk) ++{ ++ u32 *src = NULL; ++ u32 *dst = NULL; ++ ++ fk->ports.ports = 0; ++ src = (uint32_t *)psrc; ++ dst = (uint32_t *)pdst; ++ ++ if (protocol == htons(ETH_P_IP)) { ++ /* V4 addresses and address type*/ ++ fk->addrs.v4addrs.src = src[0]; ++ fk->addrs.v4addrs.dst = dst[0]; ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; ++ } else if (protocol == htons(ETH_P_IPV6)) { ++ /* V6 addresses and address type*/ ++ memcpy(&fk->addrs.v6addrs.src, src, sizeof(struct in6_addr)); ++ memcpy(&fk->addrs.v6addrs.dst, dst, sizeof(struct in6_addr)); ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; ++ } else { ++ return false; ++ } ++ if ((bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) && ++ (layer4hdr)) ++ fk->ports.ports = *layer4hdr; ++ ++ return true; ++} ++ ++/* bond_xmit_hash_without_skb - Applies load balancing algorithm for a packet, ++ * to calculate hash for a given set of L2/L3 addresses. Does not ++ * calculate egress interface. ++ */ ++uint32_t bond_xmit_hash_without_skb(u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct flow_keys flow; ++ u32 hash = 0; ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || ++ !bond_flow_dissect_without_skb(bond, src_mac, dst_mac, psrc, ++ pdst, protocol, layer4hdr, &flow)) ++ return (dst_mac[5] ^ src_mac[5]); ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23) ++ hash = dst_mac[5] ^ src_mac[5]; ++ else if (layer4hdr) ++ hash = (__force u32)flow.ports.ports; ++ ++ hash ^= (__force u32)flow_get_u32_dst(&flow) ^ ++ (__force u32)flow_get_u32_src(&flow); ++ hash ^= (hash >> 16); ++ hash ^= (hash >> 8); ++ ++ return hash; ++} ++ ++/* bond_xor_get_tx_dev - Calculate egress interface for a given packet for a LAG ++ * that is configured in balance-xor mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash_without_skb is used to calculate hash using ++ * L2/L3 addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++static struct net_device *bond_xor_get_tx_dev(struct sk_buff *skb, ++ u8 *src_mac, u8 *dst_mac, ++ void *src, void *dst, ++ u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ int slave_cnt = READ_ONCE(bond->slave_cnt); ++ int slave_id = 0, i = 0; ++ u32 hash; ++ struct list_head *iter; ++ struct slave *slave; ++ ++ if (slave_cnt == 0) { ++ pr_debug("%s: Error: No slave is attached to the interface\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_id = hash % slave_cnt; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for balance-XOR fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, src, ++ dst, protocol, bond_dev, ++ layer4hdr); ++ slave_id = hash % slave_cnt; ++ } ++ ++ i = slave_id; ++ ++ /* Here we start from the slave with slave_id */ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) { ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ } ++ ++ /* Here we start from the first slave up to slave_id */ ++ i = slave_id; ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) ++ break; ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ return NULL; ++} ++ ++/* bond_get_tx_dev - Calculate egress interface for a given packet. ++ * ++ * Supports 802.3AD and balance-xor modes ++ * ++ * @skb: pointer to skb to be egressed, if valid ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * Returns: Either valid slave device, or NULL for un-supported LAG modes ++ */ ++struct net_device *bond_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond; ++ ++ if (!bond_dev) ++ return NULL; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return NULL; ++ ++ bond = netdev_priv(bond_dev); ++ ++ switch (bond->params.mode) { ++ case BOND_MODE_XOR: ++ return bond_xor_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ case BOND_MODE_8023AD: ++ return bond_3ad_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ default: ++ return NULL; ++ } + } ++EXPORT_SYMBOL(bond_get_tx_dev); ++ ++/* In bond_xmit_xor() , we determine the output device by using a pre- ++ * determined xmit_hash_policy(), If the selected device is not enabled, ++ * find the next active slave. ++ */ ++static int bond_xmit_xor(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct bonding *bond = netdev_priv(dev); ++ struct net_device *outdev; ++ ++ outdev = bond_xor_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) ++ goto out; ++ ++ bond_dev_queue_xmit(bond, skb, outdev); ++ goto final; ++out: ++ /* no suitable interface, frame not sent */ ++ dev_kfree_skb(skb); ++final: ++ return NETDEV_TX_OK; ++} ++/* QCA NSS ECM bonding support - End */ + + static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, const void *data, + int hlen, __be16 l2_proto, int *nhoff, int *ip_proto, bool l34) +@@ -5192,15 +5482,18 @@ static netdev_tx_t bond_3ad_xor_xmit(str + struct net_device *dev) + { + struct bonding *bond = netdev_priv(dev); +- struct bond_up_slave *slaves; +- struct slave *slave; ++ /* QCA NSS ECM bonding support - Start */ ++ struct net_device *outdev = NULL; + +- slaves = rcu_dereference(bond->usable_slaves); +- slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves); +- if (likely(slave)) +- return bond_dev_queue_xmit(bond, skb, slave->dev); ++ outdev = bond_3ad_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) { ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } + +- return bond_tx_drop(dev, skb); ++ return bond_dev_queue_xmit(bond, skb, outdev); ++ /* QCA NSS ECM bonding support - End */ + } + + /* in broadcast mode, we send everything to all usable interfaces. */ +@@ -5450,8 +5743,9 @@ static netdev_tx_t __bond_start_xmit(str + return bond_xmit_roundrobin(skb, dev); + case BOND_MODE_ACTIVEBACKUP: + return bond_xmit_activebackup(skb, dev); +- case BOND_MODE_8023AD: + case BOND_MODE_XOR: ++ return bond_xmit_xor(skb, dev); /* QCA NSS ECM bonding support */ ++ case BOND_MODE_8023AD: + return bond_3ad_xor_xmit(skb, dev); + case BOND_MODE_BROADCAST: + return bond_xmit_broadcast(skb, dev); +--- a/include/net/bond_3ad.h ++++ b/include/net/bond_3ad.h +@@ -303,8 +303,15 @@ int bond_3ad_lacpdu_recv(const struct sk + int bond_3ad_set_carrier(struct bonding *bond); + void bond_3ad_update_lacp_active(struct bonding *bond); + void bond_3ad_update_lacp_rate(struct bonding *bond); ++/* QCA NSS ECM bonding support */ ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ uint8_t *dst_mac, void *src, ++ void *dst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++/* QCA NSS ECM bonding support */ ++ + void bond_3ad_update_ad_actor_settings(struct bonding *bond); + int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats); + size_t bond_3ad_stats_size(void); + #endif /* _NET_BOND_3AD_H */ +- +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -94,6 +94,8 @@ + + #define BOND_TLS_FEATURES (NETIF_F_HW_TLS_TX | NETIF_F_HW_TLS_RX) + ++extern struct bond_cb __rcu *bond_cb; /* QCA NSS ECM bonding support */ ++ + #ifdef CONFIG_NET_POLL_CONTROLLER + extern atomic_t netpoll_block_tx; + +@@ -659,6 +661,7 @@ struct bond_net { + + int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); + netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); ++int bond_get_id(struct net_device *bond_dev); /* QCA NSS ECM bonding support */ + int bond_create(struct net *net, const char *name); + int bond_create_sysfs(struct bond_net *net); + void bond_destroy_sysfs(struct bond_net *net); +@@ -689,6 +692,13 @@ struct bond_vlan_tag *bond_verify_device + int level); + int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); + void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); ++/* QCA NSS ECM bonding support - Start */ ++uint32_t bond_xmit_hash_without_skb(uint8_t *src_mac, uint8_t *dst_mac, ++ void *psrc, void *pdst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++/* QCA NSS ECM bonding support - End */ ++ + void bond_work_init_all(struct bonding *bond); + + #ifdef CONFIG_PROC_FS +@@ -793,4 +803,18 @@ static inline netdev_tx_t bond_tx_drop(s + return NET_XMIT_DROP; + } + ++/* QCA NSS ECM bonding support - Start */ ++struct bond_cb { ++ void (*bond_cb_link_up)(struct net_device *slave); ++ void (*bond_cb_link_down)(struct net_device *slave); ++ void (*bond_cb_enslave)(struct net_device *slave); ++ void (*bond_cb_release)(struct net_device *slave); ++ void (*bond_cb_delete_by_slave)(struct net_device *slave); ++ void (*bond_cb_delete_by_mac)(uint8_t *mac_addr); ++}; ++ ++extern int bond_register_cb(struct bond_cb *cb); ++extern void bond_unregister_cb(void); ++/* QCA NSS ECM bonding support - End */ ++ + #endif /* _NET_BONDING_H */ diff --git a/target/linux/qualcommax/patches-6.1/0600-5-qca-nss-ecm-support-macvlan.patch b/target/linux/qualcommax/patches-6.1/0600-5-qca-nss-ecm-support-macvlan.patch new file mode 100644 index 00000000000000..5f18c6be736aa6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-5-qca-nss-ecm-support-macvlan.patch @@ -0,0 +1,96 @@ +--- a/include/linux/if_macvlan.h ++++ b/include/linux/if_macvlan.h +@@ -15,6 +15,13 @@ struct macvlan_port; + #define MACVLAN_MC_FILTER_BITS 8 + #define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS) + ++/* QCA NSS ECM Support - Start */ ++/* ++ * Callback for updating interface statistics for macvlan flows offloaded from host CPU. ++ */ ++typedef void (*macvlan_offload_stats_update_cb_t)(struct net_device *dev, struct rtnl_link_stats64 *stats, bool update_mcast_rx_stats); ++/* QCA NSS ECM Support - End */ ++ + struct macvlan_dev { + struct net_device *dev; + struct list_head list; +@@ -35,6 +42,7 @@ struct macvlan_dev { + #ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; + #endif ++ macvlan_offload_stats_update_cb_t offload_stats_update; /* QCA NSS ECM support */ + }; + + static inline void macvlan_count_rx(const struct macvlan_dev *vlan, +@@ -107,4 +115,26 @@ static inline int macvlan_release_l2fw_o + macvlan->accel_priv = NULL; + return dev_uc_add(macvlan->lowerdev, dev->dev_addr); + } ++ ++/* QCA NSS ECM Support - Start */ ++#if IS_ENABLED(CONFIG_MACVLAN) ++static inline void ++macvlan_offload_stats_update(struct net_device *dev, ++ struct rtnl_link_stats64 *stats, ++ bool update_mcast_rx_stats) ++{ ++ struct macvlan_dev *macvlan = netdev_priv(dev); ++ ++ macvlan->offload_stats_update(dev, stats, update_mcast_rx_stats); ++} ++ ++static inline enum ++macvlan_mode macvlan_get_mode(struct net_device *dev) ++{ ++ struct macvlan_dev *macvlan = netdev_priv(dev); ++ ++ return macvlan->mode; ++} ++#endif ++/* QCA NSS ECM Support - End */ + #endif /* _LINUX_IF_MACVLAN_H */ +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -933,6 +933,34 @@ static void macvlan_uninit(struct net_de + macvlan_port_destroy(port->dev); + } + ++/* QCA NSS ECM Support - Start */ ++/* Update macvlan statistics processed by offload engines */ ++static void macvlan_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *offl_stats, ++ bool update_mcast_rx_stats) ++{ ++ struct vlan_pcpu_stats *stats; ++ struct macvlan_dev *macvlan; ++ ++ /* Is this a macvlan? */ ++ if (!netif_is_macvlan(dev)) ++ return; ++ ++ macvlan = netdev_priv(dev); ++ stats = this_cpu_ptr(macvlan->pcpu_stats); ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, offl_stats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, offl_stats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, offl_stats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, offl_stats->tx_bytes); ++ /* Update multicast statistics */ ++ if (unlikely(update_mcast_rx_stats)) { ++ u64_stats_add(&stats->rx_multicast, offl_stats->rx_packets); ++ } ++ u64_stats_update_end(&stats->syncp); ++} ++/* QCA NSS ECM Support - End */ ++ + static void macvlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -1477,6 +1505,7 @@ int macvlan_common_newlink(struct net *s + vlan->dev = dev; + vlan->port = port; + vlan->set_features = MACVLAN_FEATURES; ++ vlan->offload_stats_update = macvlan_dev_update_stats; /* QCA NSS ECM Support */ + + vlan->mode = MACVLAN_MODE_VEPA; + if (data && data[IFLA_MACVLAN_MODE]) diff --git a/target/linux/qualcommax/patches-6.1/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch b/target/linux/qualcommax/patches-6.1/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch new file mode 100644 index 00000000000000..f9480406c6f6bf --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch @@ -0,0 +1,154 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -175,6 +175,13 @@ config NF_CONNTRACK_TIMEOUT + + If unsure, say `N'. + ++config NF_CONNTRACK_DSCPREMARK_EXT ++ bool 'Connection tracking extension for dscp remark target' ++ depends on NETFILTER_ADVANCED ++ help ++ This option enables support for connection tracking extension ++ for dscp remark. ++ + config NF_CONNTRACK_TIMESTAMP + bool 'Connection tracking timestamping' + depends on NETFILTER_ADVANCED +--- a/include/net/netfilter/nf_conntrack_extend.h ++++ b/include/net/netfilter/nf_conntrack_extend.h +@@ -31,6 +31,10 @@ enum nf_ct_ext_id { + #if IS_ENABLED(CONFIG_NET_ACT_CT) + NF_CT_EXT_ACT_CT, + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ NF_CT_EXT_DSCPREMARK, /* QCA NSS ECM support */ ++#endif ++ + NF_CT_EXT_NUM, + }; + +--- a/net/netfilter/nf_conntrack_extend.c ++++ b/net/netfilter/nf_conntrack_extend.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + + #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ +@@ -54,6 +55,9 @@ static const u8 nf_ct_ext_type_len[NF_CT + #if IS_ENABLED(CONFIG_NET_ACT_CT) + [NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ [NF_CT_EXT_DSCPREMARK] = sizeof(struct nf_ct_dscpremark_ext), ++#endif + }; + + static __always_inline unsigned int total_extension_size(void) +@@ -86,6 +90,9 @@ static __always_inline unsigned int tota + #if IS_ENABLED(CONFIG_NET_ACT_CT) + + sizeof(struct nf_conn_act_ct_ext) + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ + sizeof(struct nf_ct_dscpremark_ext) ++#endif + ; + } + +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -14,6 +14,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_LABEL + nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o + nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o + nf_conntrack-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o ++nf_conntrack-$(CONFIG_NF_CONNTRACK_DSCPREMARK_EXT) += nf_conntrack_dscpremark_ext.o + ifeq ($(CONFIG_NF_CONNTRACK),m) + nf_conntrack-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_conntrack_bpf.o + else ifeq ($(CONFIG_NF_CONNTRACK),y) +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -45,6 +45,9 @@ + #include + #include + #include ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++#include ++#endif + #include + #include + #include +@@ -1781,6 +1784,9 @@ init_conntrack(struct net *net, struct n + nf_ct_acct_ext_add(ct, GFP_ATOMIC); + nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); + nf_ct_labels_ext_add(ct); ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ nf_ct_dscpremark_ext_add(ct, GFP_ATOMIC); ++#endif + + #ifdef CONFIG_NF_CONNTRACK_EVENTS + ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; +--- a/net/netfilter/xt_DSCP.c ++++ b/net/netfilter/xt_DSCP.c +@@ -15,6 +15,9 @@ + + #include + #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/qualcommax/patches-6.1/0600-7-qca-nss-ecm-fix-IPv6-user-route-change-event-calls.patch b/target/linux/qualcommax/patches-6.1/0600-7-qca-nss-ecm-fix-IPv6-user-route-change-event-calls.patch new file mode 100644 index 00000000000000..4fd75771c5a6ac --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0600-7-qca-nss-ecm-fix-IPv6-user-route-change-event-calls.patch @@ -0,0 +1,89 @@ +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(-) + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index df82117..4fb8247 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -3868,10 +3868,6 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags, + return PTR_ERR(rt); + + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); +- if (!err) +- atomic_notifier_call_chain(&ip6route_chain, +- RTM_NEWROUTE, rt); +- + fib6_info_release(rt); + + return err; +@@ -3893,9 +3889,6 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info) + err = fib6_del(rt, info); + spin_unlock_bh(&table->tb6_lock); + +- if (!err) +- atomic_notifier_call_chain(&ip6route_chain, +- RTM_DELROUTE, rt); + out: + fib6_info_release(rt); + return err; +@@ -4501,6 +4494,10 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, struct in6_rtmsg *rtmsg) + break; + } + rtnl_unlock(); ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ (cmd == SIOCADDRT) ? RTM_NEWROUTE : RTM_DELROUTE, &cfg); ++ + return err; + } + +@@ -5528,11 +5525,17 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + } + + if (cfg.fc_mp) +- return ip6_route_multipath_del(&cfg, extack); ++ err = ip6_route_multipath_del(&cfg, extack); + else { + cfg.fc_delete_all_nh = 1; +- return ip6_route_del(&cfg, extack); ++ err = ip6_route_del(&cfg, extack); + } ++ ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_DELROUTE, &cfg); ++ ++ return err; + } + + static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, +@@ -5549,9 +5552,15 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + cfg.fc_metric = IP6_RT_PRIO_USER; + + if (cfg.fc_mp) +- return ip6_route_multipath_add(&cfg, extack); ++ err = ip6_route_multipath_add(&cfg, extack); + else +- return ip6_route_add(&cfg, GFP_KERNEL, extack); ++ err = ip6_route_add(&cfg, GFP_KERNEL, extack); ++ ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_NEWROUTE, &cfg); ++ ++ return err; + } + + /* add the overhead of this fib6_nh to nexthop_len */ diff --git a/target/linux/qualcommax/patches-6.1/0601-1-qca-add-nss-bridge-mgr-support.patch b/target/linux/qualcommax/patches-6.1/0601-1-qca-add-nss-bridge-mgr-support.patch new file mode 100644 index 00000000000000..86e2f4fa562a29 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0601-1-qca-add-nss-bridge-mgr-support.patch @@ -0,0 +1,92 @@ +From 3c17a0e1112be70071e98d5208da5b55dcec20a6 Mon Sep 17 00:00:00 2001 +From: Simon Casey +Date: Wed, 2 Feb 2022 19:37:29 +0100 +Subject: [PATCH] Update 607-qca-add-add-nss-bridge-mgr-support.patch for kernel 5.15 + +--- + include/linux/if_bridge.h | 4 ++++ + net/bridge/br_fdb.c | 25 +++++++++++++++++++++---- + 2 files changed, 25 insertions(+), 4 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -252,4 +252,8 @@ typedef struct net_bridge_port *br_get_d + extern br_get_dst_hook_t __rcu *br_get_dst_hook; + /* QCA NSS ECM support - End */ + ++/* QCA NSS bridge-mgr support - Start */ ++extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); ++/* QCA NSS bridge-mgr support - End */ ++ + #endif +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -569,7 +569,7 @@ void br_fdb_cleanup(struct work_struct * + unsigned long delay = hold_time(br); + unsigned long work_delay = delay; + unsigned long now = jiffies; +- u8 mac_addr[6]; /* QCA NSS ECM support */ ++ struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */ + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -597,12 +597,13 @@ void br_fdb_cleanup(struct work_struct * + } else { + spin_lock_bh(&br->hash_lock); + if (!hlist_unhashed(&f->fdb_node)) { +- ether_addr_copy(mac_addr, f->key.addr.addr); ++ memset(&fdb_event, 0, sizeof(fdb_event)); ++ ether_addr_copy(fdb_event.addr, f->key.addr.addr); + fdb_delete(br, f, true); + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, 0, +- (void *)mac_addr); ++ (void *)&fdb_event); + /* QCA NSS ECM support - End */ + } + spin_unlock_bh(&br->hash_lock); +@@ -900,10 +901,21 @@ static bool __fdb_mark_active(struct net + test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); + } + ++/* QCA NSS bridge-mgr support - Start */ ++/* Get the bridge device */ ++struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br) ++{ ++ dev_hold(br->dev); ++ return br->dev; ++} ++EXPORT_SYMBOL_GPL(br_fdb_bridge_dev_get_and_hold); ++/* QCA NSS bridge-mgr support - End */ ++ + void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid, unsigned long flags) + { + struct net_bridge_fdb_entry *fdb; ++ struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */ + + /* some users want to always flood. */ + if (hold_time(br) == 0) +@@ -929,6 +941,12 @@ void br_fdb_update(struct net_bridge *br + if (unlikely(source != READ_ONCE(fdb->dst) && + !test_bit(BR_FDB_STICKY, &fdb->flags))) { + br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH); ++ /* QCA NSS bridge-mgr support - Start */ ++ ether_addr_copy(fdb_event.addr, addr); ++ fdb_event.br = br; ++ fdb_event.orig_dev = fdb->dst->dev; ++ fdb_event.dev = source->dev; ++ /* QCA NSS bridge-mgr support - End */ + WRITE_ONCE(fdb->dst, source); + fdb_modified = true; + /* Take over HW learned entry */ +@@ -940,7 +958,7 @@ void br_fdb_update(struct net_bridge *br + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, +- 0, (void *)addr); ++ 0, (void *)&fdb_event); + /* QCA NSS ECM support - End */ + } + diff --git a/target/linux/qualcommax/patches-6.1/0602-1-qca-nss-drv-add-qdisc-support.patch b/target/linux/qualcommax/patches-6.1/0602-1-qca-nss-drv-add-qdisc-support.patch new file mode 100644 index 00000000000000..bbffeb1f0b683b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0602-1-qca-nss-drv-add-qdisc-support.patch @@ -0,0 +1,44 @@ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -773,6 +773,7 @@ typedef unsigned char *sk_buff_data_t; + * @offload_fwd_mark: Packet was L2-forwarded in hardware + * @offload_l3_fwd_mark: Packet was L3-forwarded in hardware + * @tc_skip_classify: do not classify packet. set by IFB device ++ * @tc_skip_classify_offload: do not classify packet set by offload IFB device + * @tc_at_ingress: used within tc_classify to distinguish in/egress + * @redirected: packet was redirected by packet classifier + * @from_ingress: packet was redirected from the ingress path +@@ -968,6 +969,8 @@ struct sk_buff { + #ifdef CONFIG_NET_CLS_ACT + __u8 tc_skip_classify:1; + __u8 tc_at_ingress:1; /* See TC_AT_INGRESS_MASK */ ++ __u8 tc_skip_classify_offload:1; ++ __u16 tc_verd_qca_nss; /* QCA NSS Qdisc Support */ + #endif + #ifdef CONFIG_IPV6_NDISC_NODETYPE + __u8 ndisc_nodetype:2; +--- a/include/uapi/linux/pkt_cls.h ++++ b/include/uapi/linux/pkt_cls.h +@@ -139,6 +139,7 @@ enum tca_id { + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, ++ TCA_ID_MIRRED_NSS, /* QCA NSS Qdisc IGS Support */ + /* other actions go here */ + __TCA_ID_MAX = 255 + }; +@@ -801,4 +802,14 @@ enum { + TCF_EM_OPND_LT + }; + ++/* QCA NSS Qdisc Support - Start */ ++#define _TC_MAKE32(x) ((x)) ++#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) ++ ++#define TC_NCLS _TC_MAKEMASK1(8) ++#define TC_NCLS_NSS _TC_MAKEMASK1(12) ++#define SET_TC_NCLS_NSS(v) ( TC_NCLS_NSS | ((v) & ~TC_NCLS_NSS)) ++#define CLR_TC_NCLS_NSS(v) ( (v) & ~TC_NCLS_NSS) ++/* QCA NSS Qdisc Support - End */ ++ + #endif diff --git a/target/linux/qualcommax/patches-6.1/0603-1-qca-nss-clients-add-qdisc-support.patch b/target/linux/qualcommax/patches-6.1/0603-1-qca-nss-clients-add-qdisc-support.patch new file mode 100644 index 00000000000000..671516cfcdde9e --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-1-qca-nss-clients-add-qdisc-support.patch @@ -0,0 +1,441 @@ +--- a/include/linux/timer.h ++++ b/include/linux/timer.h +@@ -17,6 +17,7 @@ struct timer_list { + unsigned long expires; + void (*function)(struct timer_list *); + u32 flags; ++ unsigned long cust_data; + + #ifdef CONFIG_LOCKDEP + struct lockdep_map lockdep_map; +--- a/drivers/net/ifb.c ++++ b/drivers/net/ifb.c +@@ -151,6 +151,31 @@ resched: + + } + ++void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats) ++{ ++ struct ifb_dev_private *dp; ++ struct ifb_q_private *txp; ++ ++ if (!dev || !offload_stats) { ++ return; ++ } ++ ++ if (!(dev->priv_flags_ext & IFF_EXT_IFB)) { ++ return; ++ } ++ ++ dp = netdev_priv(dev); ++ txp = dp->tx_private; ++ ++ u64_stats_update_begin(&txp->rx_stats.sync); ++ txp->rx_stats.packets += u64_stats_read(&offload_stats->rx_packets); ++ txp->rx_stats.bytes += u64_stats_read(&offload_stats->rx_bytes); ++ txp->tx_stats.packets += u64_stats_read(&offload_stats->tx_packets); ++ txp->tx_stats.bytes += u64_stats_read(&offload_stats->tx_bytes); ++ u64_stats_update_end(&txp->rx_stats.sync); ++} ++EXPORT_SYMBOL(ifb_update_offload_stats); ++ + static void ifb_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -326,6 +351,7 @@ static void ifb_setup(struct net_device + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; ++ dev->priv_flags_ext |= IFF_EXT_IFB; /* Mark the device as an IFB device. */ + netif_keep_dst(dev); + eth_hw_addr_random(dev); + dev->needs_free_netdev = true; +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -4588,6 +4588,15 @@ void dev_uc_flush(struct net_device *dev + void dev_uc_init(struct net_device *dev); + + /** ++ * ifb_update_offload_stats - Update the IFB interface stats ++ * @dev: IFB device to update the stats ++ * @offload_stats: per CPU stats structure ++ * ++ * Allows update of IFB stats when flows are offloaded to an accelerator. ++ **/ ++void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats); ++ ++/** + * __dev_uc_sync - Synchonize device's unicast list + * @dev: device to sync + * @sync: function to call if address should be added +@@ -5133,6 +5142,11 @@ static inline bool netif_is_failover_sla + return dev->priv_flags & IFF_FAILOVER_SLAVE; + } + ++static inline bool netif_is_ifb_dev(const struct net_device *dev) ++{ ++ return dev->priv_flags_ext & IFF_EXT_IFB; ++} ++ + /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */ + static inline void netif_keep_dst(struct net_device *dev) + { +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -1278,4 +1278,248 @@ enum { + + #define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + ++/* QCA NSS Clients Support - Start */ ++enum { ++ TCA_NSS_ACCEL_MODE_NSS_FW, ++ TCA_NSS_ACCEL_MODE_PPE, ++ TCA_NSS_ACCEL_MODE_MAX ++}; ++ ++/* NSSFIFO section */ ++ ++enum { ++ TCA_NSSFIFO_UNSPEC, ++ TCA_NSSFIFO_PARMS, ++ __TCA_NSSFIFO_MAX ++}; ++ ++#define TCA_NSSFIFO_MAX (__TCA_NSSFIFO_MAX - 1) ++ ++struct tc_nssfifo_qopt { ++ __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRED section */ ++ ++enum { ++ TCA_NSSWRED_UNSPEC, ++ TCA_NSSWRED_PARMS, ++ __TCA_NSSWRED_MAX ++}; ++ ++#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1) ++#define NSSWRED_CLASS_MAX 6 ++struct tc_red_alg_parameter { ++ __u32 min; /* qlen_avg < min: pkts are all enqueued */ ++ __u32 max; /* qlen_avg > max: pkts are all dropped */ ++ __u32 probability;/* Drop probability at qlen_avg = max */ ++ __u32 exp_weight_factor;/* exp_weight_factor for calculate qlen_avg */ ++}; ++ ++struct tc_nsswred_traffic_class { ++ __u32 limit; /* Queue length */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* Parameters for RED alg */ ++}; ++ ++/* ++ * Weight modes for WRED ++ */ ++enum tc_nsswred_weight_modes { ++ TC_NSSWRED_WEIGHT_MODE_DSCP = 0,/* Weight mode is DSCP */ ++ TC_NSSWRED_WEIGHT_MODES, /* Must be last */ ++}; ++ ++struct tc_nsswred_qopt { ++ __u32 limit; /* Queue length */ ++ enum tc_nsswred_weight_modes weight_mode; ++ /* Weight mode */ ++ __u32 traffic_classes; /* How many traffic classes: DPs */ ++ __u32 def_traffic_class; /* Default traffic if no match: def_DP */ ++ __u32 traffic_id; /* The traffic id to be configured: DP */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* RED algorithm parameters */ ++ struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX]; ++ /* Traffic settings for dumpping */ ++ __u8 ecn; /* Setting ECN bit or dropping */ ++ __u8 set_default; /* Sets qdisc to be the default for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSCODEL section */ ++ ++enum { ++ TCA_NSSCODEL_UNSPEC, ++ TCA_NSSCODEL_PARMS, ++ __TCA_NSSCODEL_MAX ++}; ++ ++#define TCA_NSSCODEL_MAX (__TCA_NSSCODEL_MAX - 1) ++ ++struct tc_nsscodel_qopt { ++ __u32 target; /* Acceptable queueing delay */ ++ __u32 limit; /* Max number of packets that can be held in the queue */ ++ __u32 interval; /* Monitoring interval */ ++ __u32 flows; /* Number of flow buckets */ ++ __u32 quantum; /* Weight (in bytes) used for DRR of flow buckets */ ++ __u8 ecn; /* 0 - disable ECN, 1 - enable ECN */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++struct tc_nsscodel_xstats { ++ __u32 peak_queue_delay; /* Peak delay experienced by a dequeued packet */ ++ __u32 peak_drop_delay; /* Peak delay experienced by a dropped packet */ ++}; ++ ++/* NSSFQ_CODEL section */ ++ ++struct tc_nssfq_codel_xstats { ++ __u32 new_flow_count; /* Total number of new flows seen */ ++ __u32 new_flows_len; /* Current number of new flows */ ++ __u32 old_flows_len; /* Current number of old flows */ ++ __u32 ecn_mark; /* Number of packets marked with ECN */ ++ __u32 drop_overlimit; /* Number of packets dropped due to overlimit */ ++ __u32 maxpacket; /* The largest packet seen so far in the queue */ ++}; ++ ++/* NSSTBL section */ ++ ++enum { ++ TCA_NSSTBL_UNSPEC, ++ TCA_NSSTBL_PARMS, ++ __TCA_NSSTBL_MAX ++}; ++ ++#define TCA_NSSTBL_MAX (__TCA_NSSTBL_MAX - 1) ++ ++struct tc_nsstbl_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Limiting rate of TBF */ ++ __u32 peakrate; /* Maximum rate at which TBF is allowed to send */ ++ __u32 mtu; /* Max size of packet, or minumim burst size */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSPRIO section */ ++ ++#define TCA_NSSPRIO_MAX_BANDS 256 ++ ++enum { ++ TCA_NSSPRIO_UNSPEC, ++ TCA_NSSPRIO_PARMS, ++ __TCA_NSSPRIO_MAX ++}; ++ ++#define TCA_NSSPRIO_MAX (__TCA_NSSPRIO_MAX - 1) ++ ++struct tc_nssprio_qopt { ++ __u32 bands; /* Number of bands */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBF section */ ++ ++enum { ++ TCA_NSSBF_UNSPEC, ++ TCA_NSSBF_CLASS_PARMS, ++ TCA_NSSBF_QDISC_PARMS, ++ __TCA_NSSBF_MAX ++}; ++ ++#define TCA_NSSBF_MAX (__TCA_NSSBF_MAX - 1) ++ ++struct tc_nssbf_class_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 mtu; /* MTU of the associated interface */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++}; ++ ++struct tc_nssbf_qopt { ++ __u16 defcls; /* Default class value */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRR section */ ++ ++enum { ++ TCA_NSSWRR_UNSPEC, ++ TCA_NSSWRR_CLASS_PARMS, ++ TCA_NSSWRR_QDISC_PARMS, ++ __TCA_NSSWRR_MAX ++}; ++ ++#define TCA_NSSWRR_MAX (__TCA_NSSWRR_MAX - 1) ++ ++struct tc_nsswrr_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswrr_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWFQ section */ ++ ++enum { ++ TCA_NSSWFQ_UNSPEC, ++ TCA_NSSWFQ_CLASS_PARMS, ++ TCA_NSSWFQ_QDISC_PARMS, ++ __TCA_NSSWFQ_MAX ++}; ++ ++#define TCA_NSSWFQ_MAX (__TCA_NSSWFQ_MAX - 1) ++ ++struct tc_nsswfq_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswfq_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSHTB section */ ++ ++enum { ++ TCA_NSSHTB_UNSPEC, ++ TCA_NSSHTB_CLASS_PARMS, ++ TCA_NSSHTB_QDISC_PARMS, ++ __TCA_NSSHTB_MAX ++}; ++ ++#define TCA_NSSHTB_MAX (__TCA_NSSHTB_MAX - 1) ++ ++struct tc_nsshtb_class_qopt { ++ __u32 burst; /* Allowed burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 cburst; /* Maximum burst size */ ++ __u32 crate; /* Maximum bandwidth for this class */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++ __u32 priority; /* Priority value associated with this class */ ++ __u32 overhead; /* Overhead in bytes per packet */ ++}; ++ ++struct tc_nsshtb_qopt { ++ __u32 r2q; /* Rate to quantum ratio */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBLACKHOLE section */ ++ ++enum { ++ TCA_NSSBLACKHOLE_UNSPEC, ++ TCA_NSSBLACKHOLE_PARMS, ++ __TCA_NSSBLACKHOLE_MAX ++}; ++ ++#define TCA_NSSBLACKHOLE_MAX (__TCA_NSSBLACKHOLE_MAX - 1) ++ ++struct tc_nssblackhole_qopt { ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++/* QCA NSS Clients Support - End */ + #endif +--- a/net/sched/sch_api.c ++++ b/net/sched/sch_api.c +@@ -313,6 +313,7 @@ struct Qdisc *qdisc_lookup(struct net_de + out: + return q; + } ++EXPORT_SYMBOL(qdisc_lookup); + + struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) + { +@@ -2386,4 +2387,26 @@ static int __init pktsched_init(void) + return 0; + } + ++/* QCA NSS Qdisc Support - Start */ ++bool tcf_destroy(struct tcf_proto *tp, bool force) ++{ ++ tp->ops->destroy(tp, force, NULL); ++ module_put(tp->ops->owner); ++ kfree_rcu(tp, rcu); ++ ++ return true; ++} ++ ++void tcf_destroy_chain(struct tcf_proto __rcu **fl) ++{ ++ struct tcf_proto *tp; ++ ++ while ((tp = rtnl_dereference(*fl)) != NULL) { ++ RCU_INIT_POINTER(*fl, tp->next); ++ tcf_destroy(tp, true); ++ } ++} ++EXPORT_SYMBOL(tcf_destroy_chain); ++/* QCA NSS Qdisc Support - End */ ++ + subsys_initcall(pktsched_init); +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -1069,6 +1069,7 @@ static void __qdisc_destroy(struct Qdisc + + call_rcu(&qdisc->rcu, qdisc_free_cb); + } ++EXPORT_SYMBOL(qdisc_destroy); + + void qdisc_destroy(struct Qdisc *qdisc) + { +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -94,6 +94,7 @@ struct Qdisc { + #define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */ + #define TCQ_F_NOLOCK 0x100 /* qdisc does not require locking */ + #define TCQ_F_OFFLOADED 0x200 /* qdisc is offloaded to HW */ ++#define TCQ_F_NSS 0x1000 /* NSS qdisc flag. */ + u32 limit; + const struct Qdisc_ops *ops; + struct qdisc_size_table __rcu *stab; +@@ -719,6 +720,40 @@ static inline bool skb_skip_tc_classify( + return false; + } + ++/* ++ * Set skb classify bit field. ++ */ ++static inline void skb_set_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_skip_classify_offload = 1; ++#endif ++} ++ ++/* ++ * Clear skb classify bit field. ++ */ ++static inline void skb_clear_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_skip_classify_offload = 0; ++#endif ++} ++ ++/* ++ * Skip skb processing if sent from ifb dev. ++ */ ++static inline bool skb_skip_tc_classify_offload(struct sk_buff *skb) ++{ ++#ifdef CONFIG_NET_CLS_ACT ++ if (skb->tc_skip_classify_offload) { ++ skb_clear_tc_classify_offload(skb); ++ return true; ++ } ++#endif ++ return false; ++} ++ + /* Reset all TX qdiscs greater than index of a device. */ + static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) + { +@@ -1305,4 +1340,9 @@ static inline void qdisc_synchronize(con + msleep(1); + } + ++/* QCA NSS Qdisc Support - Start */ ++void qdisc_destroy(struct Qdisc *qdisc); ++void tcf_destroy_chain(struct tcf_proto __rcu **fl); ++/* QCA NSS Qdisc Support - End */ ++ + #endif diff --git a/target/linux/qualcommax/patches-6.1/0603-2-qca-nss-clients-add-l2tp-support.patch b/target/linux/qualcommax/patches-6.1/0603-2-qca-nss-clients-add-l2tp-support.patch new file mode 100644 index 00000000000000..7fa9184df25155 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-2-qca-nss-clients-add-l2tp-support.patch @@ -0,0 +1,46 @@ +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -398,6 +398,31 @@ err_tlock: + } + EXPORT_SYMBOL_GPL(l2tp_session_register); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, ++ struct l2tp_session *session, ++ struct l2tp_stats *stats) ++{ ++ atomic_long_add(atomic_long_read(&stats->rx_packets), ++ &tunnel->stats.rx_packets); ++ atomic_long_add(atomic_long_read(&stats->rx_bytes), ++ &tunnel->stats.rx_bytes); ++ atomic_long_add(atomic_long_read(&stats->tx_packets), ++ &tunnel->stats.tx_packets); ++ atomic_long_add(atomic_long_read(&stats->tx_bytes), ++ &tunnel->stats.tx_bytes); ++ ++ atomic_long_add(atomic_long_read(&stats->rx_packets), ++ &session->stats.rx_packets); ++ atomic_long_add(atomic_long_read(&stats->rx_bytes), ++ &session->stats.rx_bytes); ++ atomic_long_add(atomic_long_read(&stats->tx_packets), ++ &session->stats.tx_packets); ++ atomic_long_add(atomic_long_read(&stats->tx_bytes), ++ &session->stats.tx_bytes); ++} ++EXPORT_SYMBOL_GPL(l2tp_stats_update); ++ ++ + /***************************************************************************** + * Receive data handling + *****************************************************************************/ +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -232,6 +232,9 @@ struct l2tp_session *l2tp_session_get_nt + struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, + const char *ifname); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, ++ struct l2tp_stats *stats); ++ + /* Tunnel and session lifetime management. + * Creation of a new instance is a two-step process: create, then register. + * Destruction is triggered using the *_delete functions, and completes asynchronously. diff --git a/target/linux/qualcommax/patches-6.1/0603-3-qca-nss-clients-add-PPTP-support.patch b/target/linux/qualcommax/patches-6.1/0603-3-qca-nss-clients-add-PPTP-support.patch new file mode 100644 index 00000000000000..5fb9917bc9067e --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-3-qca-nss-clients-add-PPTP-support.patch @@ -0,0 +1,478 @@ +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -36,6 +36,7 @@ struct pptp_opt { + u32 ack_sent, ack_recv; + u32 seq_sent, seq_recv; + int ppp_flags; ++ bool pptp_offload_mode; + }; + #include + +@@ -100,8 +101,40 @@ struct pppoe_channel_ops { + int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *); + }; + ++/* PPTP client callback */ ++typedef int (*pptp_gre_seq_offload_callback_t)(struct sk_buff *skb, ++ struct net_device *pptp_dev); ++ + /* Return PPPoE channel specific addressing information */ + extern int pppoe_channel_addressing_get(struct ppp_channel *chan, + struct pppoe_opt *addressing); + ++/* Lookup PPTP session info and return PPTP session using sip, dip and local call id */ ++extern int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr); ++ ++/* Lookup PPTP session info and return PPTP session using dip and peer call id */ ++extern int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Return PPTP session information given the channel */ ++extern void pptp_channel_addressing_get(struct pptp_opt *opt, ++ struct ppp_channel *chan); ++ ++/* Enable the PPTP session offload flag */ ++extern int pptp_session_enable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Disable the PPTP session offload flag */ ++extern int pptp_session_disable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Register the PPTP GRE packets sequence number offload callback */ ++extern int ++pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_client_cb); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++extern void pptp_unregister_gre_seq_offload_callback(void); ++ + #endif /* !(__LINUX_IF_PPPOX_H) */ +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -2973,6 +2973,20 @@ char *ppp_dev_name(struct ppp_channel *c + return name; + } + ++/* Return the PPP net device index */ ++int ppp_dev_index(struct ppp_channel *chan) ++{ ++ struct channel *pch = chan->ppp; ++ int ifindex = 0; ++ ++ if (pch) { ++ read_lock_bh(&pch->upl); ++ if (pch->ppp && pch->ppp->dev) ++ ifindex = pch->ppp->dev->ifindex; ++ read_unlock_bh(&pch->upl); ++ } ++ return ifindex; ++} + + /* + * Disconnect a channel from the generic layer. +@@ -3681,6 +3695,28 @@ void ppp_update_stats(struct net_device + ppp_recv_unlock(ppp); + } + ++/* Returns true if Compression is enabled on PPP device ++ */ ++bool ppp_is_cp_enabled(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ bool flag = false; ++ ++ if (!dev) ++ return false; ++ ++ if (dev->type != ARPHRD_PPP) ++ return false; ++ ++ ppp = netdev_priv(dev); ++ ppp_lock(ppp); ++ flag = !!(ppp->xstate & SC_COMP_RUN) || !!(ppp->rstate & SC_DECOMP_RUN); ++ ppp_unlock(ppp); ++ ++ return flag; ++} ++EXPORT_SYMBOL(ppp_is_cp_enabled); ++ + /* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if + * the device is not PPP. + */ +@@ -3872,6 +3908,7 @@ EXPORT_SYMBOL(ppp_unregister_channel); + EXPORT_SYMBOL(ppp_channel_index); + EXPORT_SYMBOL(ppp_unit_number); + EXPORT_SYMBOL(ppp_dev_name); ++EXPORT_SYMBOL(ppp_dev_index); + EXPORT_SYMBOL(ppp_input); + EXPORT_SYMBOL(ppp_input_error); + EXPORT_SYMBOL(ppp_output_wakeup); +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -84,6 +84,9 @@ extern void ppp_unregister_channel(struc + /* Get the channel number for a channel */ + extern int ppp_channel_index(struct ppp_channel *); + ++/* Get the device index associated with a channel, or 0, if none */ ++extern int ppp_dev_index(struct ppp_channel *); ++ + /* Get the unit number associated with a channel, or -1 if none */ + extern int ppp_unit_number(struct ppp_channel *); + +@@ -116,6 +119,7 @@ extern int ppp_hold_channels(struct net_ + /* Test if ppp xmit lock is locked */ + extern bool ppp_is_xmit_locked(struct net_device *dev); + ++bool ppp_is_cp_enabled(struct net_device *dev); + /* Test if the ppp device is a multi-link ppp device */ + extern int ppp_is_multilink(struct net_device *dev); + +--- a/drivers/net/ppp/pptp.c ++++ b/drivers/net/ppp/pptp.c +@@ -50,6 +50,8 @@ static struct proto pptp_sk_proto __read + static const struct ppp_channel_ops pptp_chan_ops; + static const struct proto_ops pptp_ops; + ++static pptp_gre_seq_offload_callback_t __rcu pptp_gre_offload_xmit_cb; ++ + static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) + { + struct pppox_sock *sock; +@@ -91,6 +93,79 @@ static int lookup_chan_dst(u16 call_id, + return i < MAX_CALLID; + } + ++/* Search a pptp session based on local call id, local and remote ip address */ ++static int lookup_session_src(struct pptp_opt *opt, u16 call_id, __be32 daddr, __be32 saddr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.src_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == daddr && ++ sock->proto.pptp.src_addr.sin_addr.s_addr == saddr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Search a pptp session based on peer call id and peer ip address */ ++static int lookup_session_dst(struct pptp_opt *opt, u16 call_id, __be32 d_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == d_addr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* If offload mode set then this function sends all packets to ++ * offload module instead of network stack ++ */ ++static int pptp_client_skb_xmit(struct sk_buff *skb, ++ struct net_device *pptp_dev) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ int ret; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (!pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ ret = pptp_gre_offload_cb_f(skb, pptp_dev); ++ rcu_read_unlock(); ++ return ret; ++} ++ + static int add_chan(struct pppox_sock *sock, + struct pptp_addr *sa) + { +@@ -136,7 +211,7 @@ static struct rtable *pptp_route_output( + struct net *net; + + net = sock_net(sk); +- flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0, ++ flowi4_init_output(fl4, 0, sk->sk_mark, 0, + RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0, + po->proto.pptp.dst_addr.sin_addr.s_addr, + po->proto.pptp.src_addr.sin_addr.s_addr, +@@ -163,8 +238,11 @@ static int pptp_xmit(struct ppp_channel + + struct rtable *rt; + struct net_device *tdev; ++ struct net_device *pptp_dev; + struct iphdr *iph; + int max_headroom; ++ int pptp_ifindex; ++ int ret; + + if (sk_pppox(po)->sk_state & PPPOX_DEAD) + goto tx_error; +@@ -258,7 +336,32 @@ static int pptp_xmit(struct ppp_channel + ip_select_ident(net, skb, NULL); + ip_send_check(iph); + +- ip_local_out(net, skb->sk, skb); ++ pptp_ifindex = ppp_dev_index(chan); ++ ++ /* set incoming interface as the ppp interface */ ++ if (skb->skb_iif) ++ skb->skb_iif = pptp_ifindex; ++ ++ /* If the PPTP GRE seq number offload module is not enabled yet ++ * then sends all PPTP GRE packets through linux network stack ++ */ ++ if (!opt->pptp_offload_mode) { ++ ip_local_out(net, skb->sk, skb); ++ return 1; ++ } ++ ++ pptp_dev = dev_get_by_index(&init_net, pptp_ifindex); ++ if (!pptp_dev) ++ goto tx_error; ++ ++ /* If PPTP offload module is enabled then forward all PPTP GRE ++ * packets to PPTP GRE offload module ++ */ ++ ret = pptp_client_skb_xmit(skb, pptp_dev); ++ dev_put(pptp_dev); ++ if (ret < 0) ++ goto tx_error; ++ + return 1; + + tx_error: +@@ -314,6 +417,13 @@ static int pptp_rcv_core(struct sock *sk + goto drop; + + payload = skb->data + headersize; ++ ++ /* If offload is enabled, we expect the offload module ++ * to handle PPTP GRE sequence number checks ++ */ ++ if (opt->pptp_offload_mode) ++ goto allow_packet; ++ + /* check for expected sequence number */ + if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { + if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && +@@ -371,6 +481,7 @@ static int pptp_rcv(struct sk_buff *skb) + if (po) { + skb_dst_drop(skb); + nf_reset_ct(skb); ++ skb->skb_iif = ppp_dev_index(&po->chan); + return sk_receive_skb(sk_pppox(po), skb, 0); + } + drop: +@@ -473,7 +584,7 @@ static int pptp_connect(struct socket *s + + opt->dst_addr = sp->sa_addr.pptp; + sk->sk_state |= PPPOX_CONNECTED; +- ++ opt->pptp_offload_mode = false; + end: + release_sock(sk); + return error; +@@ -603,9 +714,169 @@ static int pptp_ppp_ioctl(struct ppp_cha + return err; + } + ++/* pptp_channel_addressing_get() ++ * Return PPTP channel specific addressing information. ++ */ ++void pptp_channel_addressing_get(struct pptp_opt *opt, struct ppp_channel *chan) ++{ ++ struct sock *sk; ++ struct pppox_sock *po; ++ ++ if (!opt) ++ return; ++ ++ sk = (struct sock *)chan->private; ++ if (!sk) ++ return; ++ ++ sock_hold(sk); ++ ++ /* This is very unlikely, but check the socket is connected state */ ++ if (unlikely(sock_flag(sk, SOCK_DEAD) || ++ !(sk->sk_state & PPPOX_CONNECTED))) { ++ sock_put(sk); ++ return; ++ } ++ ++ po = pppox_sk(sk); ++ memcpy(opt, &po->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk); ++} ++EXPORT_SYMBOL(pptp_channel_addressing_get); ++ ++/* pptp_session_find() ++ * Search and return a PPTP session info based on peer callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_dst(opt, ntohs(peer_call_id), peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_find); ++ ++/* pptp_session_find_by_src_callid() ++ * Search and return a PPTP session info based on src callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_src(opt, ntohs(src_call_id), daddr, saddr); ++} ++EXPORT_SYMBOL(pptp_session_find_by_src_callid); ++ ++ /* Function to change the offload mode true/false for a PPTP session */ ++static int pptp_set_offload_mode(bool accel_mode, ++ __be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == peer_call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == peer_ip_addr) { ++ sock_hold(sk_pppox(sock)); ++ sock->proto.pptp.pptp_offload_mode = accel_mode; ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Enable the PPTP session offload flag */ ++int pptp_session_enable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(true, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_enable_offload_mode); ++ ++/* Disable the PPTP session offload flag */ ++int pptp_session_disable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(false, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_disable_offload_mode); ++ ++/* Register the offload callback function on behalf of the module which ++ * will own the sequence and acknowledgment number updates for all ++ * PPTP GRE packets. All PPTP GRE packets are then transmitted to this ++ * module after encapsulation in order to ensure the correct seq/ack ++ * fields are set in the packets before transmission. This is required ++ * when PPTP flows are offloaded to acceleration engines, in-order to ++ * ensure consistency in sequence and ack numbers between PPTP control ++ * (PPP LCP) and data packets ++ */ ++int pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_gre_offload_cb) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, pptp_gre_offload_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(pptp_register_gre_seq_offload_callback); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++void pptp_unregister_gre_seq_offload_callback(void) ++{ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, NULL); ++} ++EXPORT_SYMBOL(pptp_unregister_gre_seq_offload_callback); ++ ++/* pptp_hold_chan() */ ++static void pptp_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/* pptp_release_chan() */ ++static void pptp_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/* pptp_get_channel_protocol() ++ * Return the protocol type of the PPTP over PPP protocol ++ */ ++static int pptp_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_PPTP; ++} ++ + static const struct ppp_channel_ops pptp_chan_ops = { + .start_xmit = pptp_xmit, + .ioctl = pptp_ppp_ioctl, ++ .get_channel_protocol = pptp_get_channel_protocol, ++ .hold = pptp_hold_chan, ++ .release = pptp_release_chan, + }; + + static struct proto pptp_sk_proto __read_mostly = { diff --git a/target/linux/qualcommax/patches-6.1/0603-4-qca-nss-clients-add-iptunnel-support.patch b/target/linux/qualcommax/patches-6.1/0603-4-qca-nss-clients-add-iptunnel-support.patch new file mode 100644 index 00000000000000..928510087da42a --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-4-qca-nss-clients-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 +@@ -553,4 +553,9 @@ static inline void ip_tunnel_info_opts_s + + #endif /* CONFIG_INET */ + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr); ++void ip6_update_offload_stats(struct net_device *dev, void *ptr); ++/* QCA NSS Clients Support - End */ ++ + #endif /* __NET_IP_TUNNELS_H */ +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -2398,6 +2398,26 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Client Support - Start */ ++/* ++ * Update offload stats ++ */ ++void ip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ip6_update_offload_stats); ++/* QCA NSS Client Support - End */ ++ + struct net *ip6_tnl_get_link_net(const struct net_device *dev) + { + struct ip6_tnl *tunnel = netdev_priv(dev); +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1733,6 +1733,23 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ipip6_update_offload_stats); ++/* QCA NSS Clients Support - End */ ++ + static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, diff --git a/target/linux/qualcommax/patches-6.1/0603-5-qca-nss-clients-add-vxlan-support.patch b/target/linux/qualcommax/patches-6.1/0603-5-qca-nss-clients-add-vxlan-support.patch new file mode 100644 index 00000000000000..4704b7d24895e9 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-5-qca-nss-clients-add-vxlan-support.patch @@ -0,0 +1,103 @@ +--- a/drivers/net/vxlan/vxlan_core.c ++++ b/drivers/net/vxlan/vxlan_core.c +@@ -71,6 +71,20 @@ static inline bool vxlan_collect_metadat + ip_tunnel_collect_metadata(); + } + ++ATOMIC_NOTIFIER_HEAD(vxlan_fdb_notifier_list); ++ ++void vxlan_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_register_notify); ++ ++void vxlan_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_unregister_notify); ++ + #if IS_ENABLED(CONFIG_IPV6) + static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla) + { +@@ -307,6 +321,7 @@ static void __vxlan_fdb_notify(struct vx + { + struct net *net = dev_net(vxlan->dev); + struct sk_buff *skb; ++ struct vxlan_fdb_event vfe; + int err = -ENOBUFS; + + skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC); +@@ -322,6 +337,10 @@ static void __vxlan_fdb_notify(struct vx + } + + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); ++ vfe.dev = vxlan->dev; ++ vfe.rdst = rd; ++ ether_addr_copy(vfe.eth_addr, fdb->eth_addr); ++ atomic_notifier_call_chain(&vxlan_fdb_notifier_list, type, (void *)&vfe); + return; + errout: + if (err < 0) +@@ -488,6 +507,18 @@ static struct vxlan_fdb *vxlan_find_mac( + return f; + } + ++/* Find and update age of fdb entry corresponding to MAC. */ ++void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni) ++{ ++ u32 hash_index; ++ ++ hash_index = fdb_head_index(vxlan, mac, vni); ++ spin_lock_bh(&vxlan->hash_lock[hash_index]); ++ vxlan_find_mac(vxlan, mac, vni); ++ spin_unlock_bh(&vxlan->hash_lock[hash_index]); ++} ++EXPORT_SYMBOL(vxlan_fdb_update_mac); ++ + /* caller should hold vxlan->hash_lock */ + static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f, + union vxlan_addr *ip, __be16 port, +@@ -2658,6 +2689,9 @@ static void vxlan_xmit_one(struct sk_buf + goto out_unlock; + } + ++ /* Reset the skb_iif to Tunnels interface index */ ++ skb->skb_iif = dev->ifindex; ++ + tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); + err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), +@@ -2729,6 +2763,9 @@ static void vxlan_xmit_one(struct sk_buf + if (err < 0) + goto tx_error; + ++ /* Reset the skb_iif to Tunnels interface index */ ++ skb->skb_iif = dev->ifindex; ++ + udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, + &local_ip.sin6.sin6_addr, + &dst->sin6.sin6_addr, tos, ttl, +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -344,6 +344,19 @@ struct vxlan_dev { + VXLAN_F_COLLECT_METADATA | \ + VXLAN_F_VNIFILTER) + ++/* ++ * Application data for fdb notifier event ++ */ ++struct vxlan_fdb_event { ++ struct net_device *dev; ++ struct vxlan_rdst *rdst; ++ u8 eth_addr[ETH_ALEN]; ++}; ++ ++extern void vxlan_fdb_register_notify(struct notifier_block *nb); ++extern void vxlan_fdb_unregister_notify(struct notifier_block *nb); ++extern void vxlan_fdb_update_mac(struct vxlan_dev *vxlan, const u8 *mac, uint32_t vni); ++ + struct net_device *vxlan_dev_create(struct net *net, const char *name, + u8 name_assign_type, struct vxlan_config *conf); + diff --git a/target/linux/qualcommax/patches-6.1/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch b/target/linux/qualcommax/patches-6.1/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch new file mode 100644 index 00000000000000..4032eb3c227134 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch @@ -0,0 +1,368 @@ +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -61,6 +61,51 @@ struct ppp_channel { + }; + + #ifdef __KERNEL__ ++/* Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ */ ++extern int ppp_channel_get_protocol(struct ppp_channel *); ++ ++/* Call this to hold a channel */ ++extern bool ppp_channel_hold(struct ppp_channel *); ++ ++/* Call this to release a hold you have upon a channel */ ++extern void ppp_channel_release(struct ppp_channel *); ++ ++/* Release hold on PPP channels */ ++extern void ppp_release_channels(struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if ppp xmit lock is locked */ ++extern bool ppp_is_xmit_locked(struct net_device *dev); ++ ++/* Call this get protocol version */ ++extern int ppp_channel_get_proto_version(struct ppp_channel *); ++ ++/* Get the device index associated with a channel, or 0, if none */ ++extern int ppp_dev_index(struct ppp_channel *); ++ ++/* Hold PPP channels for the PPP device */ ++extern int ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++extern int __ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int ppp_is_multilink(struct net_device *dev); ++extern int __ppp_is_multilink(struct net_device *dev); ++ ++/* Update statistics of the PPP net_device by incrementing related ++ * statistics field value with corresponding parameter ++ */ ++extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped); ++ + /* Called by the channel when it can send some more data. */ + extern void ppp_output_wakeup(struct ppp_channel *); + +@@ -148,5 +193,17 @@ extern void ppp_update_stats(struct net_ + * that ppp_unregister_channel returns. + */ + ++/* QCA NSS Clients Support - Start */ ++/* PPP channel connection event types */ ++#define PPP_CHANNEL_DISCONNECT 0 ++#define PPP_CHANNEL_CONNECT 1 ++ ++/* Register the PPP channel connect notifier */ ++extern void ppp_channel_connection_register_notify(struct notifier_block *nb); ++ ++/* Unregister the PPP channel connect notifier */ ++extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb); ++/* QCA NSS Clients Support - End */ ++ + #endif /* __KERNEL__ */ + #endif +--- a/include/linux/if_pppol2tp.h ++++ b/include/linux/if_pppol2tp.h +@@ -12,4 +12,30 @@ + #include + #include + ++/* QCA NSS ECM support - Start */ ++/* ++ * Holds L2TP channel info ++ */ ++struct pppol2tp_common_addr { ++ int tunnel_version; /* v2 or v3 */ ++ __u32 local_tunnel_id, remote_tunnel_id; /* tunnel id */ ++ __u32 local_session_id, remote_session_id; /* session id */ ++ struct sockaddr_in local_addr, remote_addr; /* ip address and port */ ++}; ++ ++/* ++ * L2TP channel operations ++ */ ++struct pppol2tp_channel_ops { ++ struct ppp_channel_ops ops; /* ppp channel ops */ ++}; ++ ++/* ++ * exported function which calls pppol2tp channel's get addressing ++ * function ++ */ ++extern int pppol2tp_channel_addressing_get(struct ppp_channel *, ++ struct pppol2tp_common_addr *); ++/* QCA NSS ECM support - End */ ++ + #endif +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -123,9 +123,17 @@ struct pppol2tp_session { + }; + + static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); +- +-static const struct ppp_channel_ops pppol2tp_chan_ops = { +- .start_xmit = pppol2tp_xmit, ++static int pppol2tp_get_channel_protocol(struct ppp_channel *); ++static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *); ++static void pppol2tp_hold_chan(struct ppp_channel *); ++static void pppol2tp_release_chan(struct ppp_channel *); ++ ++static const struct pppol2tp_channel_ops pppol2tp_chan_ops = { ++ .ops.start_xmit = pppol2tp_xmit, ++ .ops.get_channel_protocol = pppol2tp_get_channel_protocol, ++ .ops.get_channel_protocol_ver = pppol2tp_get_channel_protocol_ver, ++ .ops.hold = pppol2tp_hold_chan, ++ .ops.release = pppol2tp_release_chan, + }; + + static const struct proto_ops pppol2tp_ops; +@@ -373,6 +381,13 @@ static int pppol2tp_xmit(struct ppp_chan + skb->data[0] = PPP_ALLSTATIONS; + skb->data[1] = PPP_UI; + ++ /* QCA NSS ECM support - start */ ++ /* set incoming interface as the ppp interface */ ++ if ((skb->protocol == htons(ETH_P_IP)) || ++ (skb->protocol == htons(ETH_P_IPV6))) ++ skb->skb_iif = ppp_dev_index(chan); ++ /* QCA NSS ECM support - End */ ++ + local_bh_disable(); + l2tp_xmit_skb(session, skb); + local_bh_enable(); +@@ -818,7 +833,7 @@ static int pppol2tp_connect(struct socke + po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; + + po->chan.private = sk; +- po->chan.ops = &pppol2tp_chan_ops; ++ po->chan.ops = (struct ppp_channel_ops *)&pppol2tp_chan_ops.ops; + po->chan.mtu = pppol2tp_tunnel_mtu(tunnel); + + error = ppp_register_net_channel(sock_net(sk), &po->chan); +@@ -1732,6 +1747,109 @@ static void __exit pppol2tp_exit(void) + unregister_pernet_device(&pppol2tp_net_ops); + } + ++/* QCA NSS ECM support - Start */ ++/* pppol2tp_hold_chan() */ ++static void pppol2tp_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/* pppol2tp_release_chan() */ ++static void pppol2tp_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/* pppol2tp_get_channel_protocol() ++ * Return the protocol type of the L2TP over PPP protocol ++ */ ++static int pppol2tp_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_OL2TP; ++} ++ ++/* pppol2tp_get_channel_protocol_ver() ++ * Return the protocol version of the L2TP over PPP protocol ++ */ ++static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *chan) ++{ ++ struct sock *sk; ++ struct l2tp_session *session; ++ struct l2tp_tunnel *tunnel; ++ int version = 0; ++ ++ if (chan && chan->private) ++ sk = (struct sock *)chan->private; ++ else ++ return -1; ++ ++ /* Get session and tunnel contexts from the socket */ ++ session = pppol2tp_sock_to_session(sk); ++ if (!session) ++ return -1; ++ ++ tunnel = session->tunnel; ++ if (!tunnel) { ++ sock_put(sk); ++ return -1; ++ } ++ ++ version = tunnel->version; ++ ++ sock_put(sk); ++ ++ return version; ++} ++ ++/* pppol2tp_get_addressing() */ ++static int pppol2tp_get_addressing(struct ppp_channel *chan, ++ struct pppol2tp_common_addr *addr) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ struct l2tp_session *session; ++ struct l2tp_tunnel *tunnel; ++ struct inet_sock *isk = NULL; ++ int err = -ENXIO; ++ ++ /* Get session and tunnel contexts from the socket */ ++ session = pppol2tp_sock_to_session(sk); ++ if (!session) ++ return err; ++ ++ tunnel = session->tunnel; ++ if (!tunnel) { ++ sock_put(sk); ++ return err; ++ } ++ isk = inet_sk(tunnel->sock); ++ ++ addr->local_tunnel_id = tunnel->tunnel_id; ++ addr->remote_tunnel_id = tunnel->peer_tunnel_id; ++ addr->local_session_id = session->session_id; ++ addr->remote_session_id = session->peer_session_id; ++ ++ addr->local_addr.sin_port = isk->inet_sport; ++ addr->remote_addr.sin_port = isk->inet_dport; ++ addr->local_addr.sin_addr.s_addr = isk->inet_saddr; ++ addr->remote_addr.sin_addr.s_addr = isk->inet_daddr; ++ ++ sock_put(sk); ++ return 0; ++} ++ ++/* pppol2tp_channel_addressing_get() */ ++int pppol2tp_channel_addressing_get(struct ppp_channel *chan, ++ struct pppol2tp_common_addr *addr) ++{ ++ return pppol2tp_get_addressing(chan, addr); ++} ++EXPORT_SYMBOL(pppol2tp_channel_addressing_get); ++/* QCA NSS ECM support - End */ ++ + module_init(pppol2tp_init); + module_exit(pppol2tp_exit); + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -3743,6 +3743,32 @@ int ppp_is_multilink(struct net_device * + } + EXPORT_SYMBOL(ppp_is_multilink); + ++/* __ppp_is_multilink() ++ * Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 ++ * if the device is not PPP. Caller should acquire ppp_lock before calling ++ * this function ++ */ ++int __ppp_is_multilink(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ unsigned int flags; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ flags = ppp->flags; ++ ++ if (flags & SC_MULTILINK) ++ return 1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(__ppp_is_multilink); ++ + /* ppp_channel_get_protocol() + * Call this to obtain the underlying protocol of the PPP channel, + * e.g. PX_PROTO_OE +@@ -3881,6 +3907,59 @@ int ppp_hold_channels(struct net_device + } + EXPORT_SYMBOL(ppp_hold_channels); + ++/* __ppp_hold_channels() ++ * Returns the PPP channels of the PPP device, storing each one into ++ * channels[]. ++ * ++ * channels[] has chan_sz elements. ++ * This function returns the number of channels stored, up to chan_sz. ++ * It will return < 0 if the device is not PPP. ++ * ++ * You MUST release the channels using ppp_release_channels(). ++ */ ++int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], ++ unsigned int chan_sz) ++{ ++ struct ppp *ppp; ++ int c; ++ struct channel *pch; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ++ c = 0; ++ list_for_each_entry(pch, &ppp->channels, clist) { ++ struct ppp_channel *chan; ++ ++ if (!pch->chan) { ++ /* Channel is going / gone away */ ++ continue; ++ } ++ ++ if (c == chan_sz) { ++ /* No space to record channel */ ++ return c; ++ } ++ ++ /* Hold the channel, if supported */ ++ chan = pch->chan; ++ if (!chan->ops->hold) ++ continue; ++ ++ chan->ops->hold(chan); ++ ++ /* Record the channel */ ++ channels[c++] = chan; ++ } ++ return c; ++} ++EXPORT_SYMBOL(__ppp_hold_channels); ++ + /* ppp_release_channels() + * Releases channels + */ +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -235,6 +235,9 @@ struct l2tp_session *l2tp_session_get_by + void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, + struct l2tp_stats *stats); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, ++ struct l2tp_stats *stats); ++ + /* Tunnel and session lifetime management. + * Creation of a new instance is a two-step process: create, then register. + * Destruction is triggered using the *_delete functions, and completes asynchronously. diff --git a/target/linux/qualcommax/patches-6.1/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch b/target/linux/qualcommax/patches-6.1/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch new file mode 100644 index 00000000000000..faba23d8630d8c --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch @@ -0,0 +1,22 @@ +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -2404,7 +2404,7 @@ nla_put_failure: + */ + void ip6_update_offload_stats(struct net_device *dev, void *ptr) + { +- struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); + const struct pcpu_sw_netstats *offload_stats = + (struct pcpu_sw_netstats *)ptr; + +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1736,7 +1736,7 @@ nla_put_failure: + /* QCA NSS Clients Support - Start */ + void ipip6_update_offload_stats(struct net_device *dev, void *ptr) + { +- struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); + const struct pcpu_sw_netstats *offload_stats = + (struct pcpu_sw_netstats *)ptr; + diff --git a/target/linux/qualcommax/patches-6.1/0603-8-qca-nss-clients-add-tls-mgr-support.patch b/target/linux/qualcommax/patches-6.1/0603-8-qca-nss-clients-add-tls-mgr-support.patch new file mode 100644 index 00000000000000..0499e237f6be88 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0603-8-qca-nss-clients-add-tls-mgr-support.patch @@ -0,0 +1,24 @@ +--- /dev/null ++++ b/include/uapi/linux/tlshdr.h +@@ -0,0 +1,21 @@ ++#ifndef _UAPI_LINUX_TLSHDR_H ++#define _UAPI_LINUX_TLSHDR_H ++ ++#include ++ ++struct tlshdr { ++ __u8 type; ++ __be16 version; ++ __be16 len; ++} __attribute__((packed)); ++ ++#define TLSHDR_REC_TYPE_CCS 20 /* TLS packet is change cipher specification */ ++#define TLSHDR_REC_TYPE_ALERT 21 /* TLS packet is Alert */ ++#define TLSHDR_REC_TYPE_HANDSHAKE 22 /* TLS packet is Handshake */ ++#define TLSHDR_REC_TYPE_DATA 23 /* TLS packet is Application data */ ++ ++#define TLSHDR_VERSION_1_1 0x0302 /* TLS Header Version(tls 1.1) */ ++#define TLSHDR_VERSION_1_2 0x0303 /* TLS Header Version(tls 1.2) */ ++#define TLSHDR_VERSION_1_3 0x0304 /* TLS Header Version(tls 1.3) */ ++ ++#endif /* _UAPI_LINUX_TLSHDR_H */ diff --git a/target/linux/qualcommax/patches-6.1/0604-1-qca-add-mcs-support.patch b/target/linux/qualcommax/patches-6.1/0604-1-qca-add-mcs-support.patch new file mode 100644 index 00000000000000..7035383acd67c1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0604-1-qca-add-mcs-support.patch @@ -0,0 +1,876 @@ +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -281,4 +281,17 @@ extern br_get_dst_hook_t __rcu *br_get_d + extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); + /* QCA NSS bridge-mgr support - End */ + ++/* QCA qca-mcs support - Start */ ++typedef struct net_bridge_port *br_get_dst_hook_t(const struct net_bridge_port *src, ++ struct sk_buff **skb); ++extern br_get_dst_hook_t __rcu *br_get_dst_hook; ++ ++typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, ++ struct sk_buff *skb); ++extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; ++ ++typedef void (br_notify_hook_t)(int group, int event, const void *ptr); ++extern br_notify_hook_t __rcu *br_notify_hook; ++/* QCA qca-mcs support - End */ ++ + #endif +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -232,6 +232,8 @@ static void fdb_notify(struct net_bridge + kfree_skb(skb); + goto errout; + } ++ ++ __br_notify(RTNLGRP_NEIGH, type, fdb); /* QCA qca-mcs support */ + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + return; + errout: +@@ -298,6 +300,7 @@ struct net_bridge_fdb_entry *br_fdb_find + { + return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); + } ++EXPORT_SYMBOL_GPL(br_fdb_find_rcu); /* QCA qca-mcs support */ + + /* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -853,6 +853,7 @@ void br_manage_promisc(struct net_bridge + int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); + + /* br_input.c */ ++int br_pass_frame_up(struct sk_buff *skb); /* QCA qca-mcs support */ + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); + rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); + +@@ -2178,4 +2179,14 @@ struct nd_msg *br_is_nd_neigh_msg(struct + #define __br_get(__hook, __default, __args ...) \ + (__hook ? (__hook(__args)) : (__default)) + /* QCA NSS ECM support - End */ ++ ++/* QCA qca-mcs support - Start */ ++static inline void __br_notify(int group, int type, const void *data) ++{ ++ br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook); ++ ++ if (notify_hook) ++ notify_hook(group, type, data); ++} ++/* QCA qca-mcs support - End */ + #endif +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -640,6 +640,7 @@ void br_info_notify(int event, const str + kfree_skb(skb); + goto errout; + } ++ __br_notify(RTNLGRP_LINK, event, port); /* QCA qca-mcs support */ + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + return; + errout: +--- a/net/bridge/br.c ++++ b/net/bridge/br.c +@@ -467,6 +467,12 @@ static void __exit br_deinit(void) + br_fdb_fini(); + } + ++/* QCA qca-mcs support - Start */ ++/* Hook for bridge event notifications */ ++br_notify_hook_t __rcu *br_notify_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_notify_hook); ++/* QCA qca-mcs support - End */ ++ + module_init(br_init) + module_exit(br_deinit) + MODULE_LICENSE("GPL"); +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -82,6 +82,13 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + if (is_broadcast_ether_addr(dest)) { + br_flood(br, skb, BR_PKT_BROADCAST, false, true); + } else if (is_multicast_ether_addr(dest)) { ++ /* QCA qca-mcs support - Start */ ++ br_multicast_handle_hook_t *multicast_handle_hook = ++ rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, NULL, skb)) ++ goto out; ++ /* QCA qca-mcs support - End */ ++ + if (unlikely(netpoll_tx_running(dev))) { + br_flood(br, skb, BR_PKT_MULTICAST, false, true); + goto out; +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -30,7 +30,17 @@ br_netif_receive_skb(struct net *net, st + return netif_receive_skb(skb); + } + +-static int br_pass_frame_up(struct sk_buff *skb) ++/* QCA qca-mcs support - Start */ ++/* Hook for external Multicast handler */ ++br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_multicast_handle_hook); ++ ++/* Hook for external forwarding logic */ ++br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_get_dst_hook); ++/* QCA qca-mcs support - End */ ++ ++int br_pass_frame_up(struct sk_buff *skb) + { + struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; + struct net_bridge *br = netdev_priv(brdev); +@@ -69,6 +79,7 @@ static int br_pass_frame_up(struct sk_bu + dev_net(indev), NULL, skb, indev, NULL, + br_netif_receive_skb); + } ++EXPORT_SYMBOL_GPL(br_pass_frame_up); /* QCA qca-mcs support */ + + /* note: already called with rcu_read_lock */ + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +@@ -82,6 +93,11 @@ int br_handle_frame_finish(struct net *n + struct net_bridge_mcast *brmctx; + struct net_bridge_vlan *vlan; + struct net_bridge *br; ++ /* QCA qca-mcs support - Start */ ++ br_multicast_handle_hook_t *multicast_handle_hook; ++ struct net_bridge_port *pdst = NULL; ++ br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); ++ /* QCA qca-mcs support - End */ + u16 vid = 0; + u8 state; + +@@ -158,6 +174,12 @@ int br_handle_frame_finish(struct net *n + + switch (pkt_type) { + case BR_PKT_MULTICAST: ++ /* QCA qca-mcs support - Start */ ++ multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, p, skb)) ++ goto out; ++ /* QCA qca-mcs support - End */ ++ + mdst = br_mdb_get(brmctx, skb, vid); + if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && + br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { +@@ -173,8 +195,15 @@ int br_handle_frame_finish(struct net *n + } + break; + case BR_PKT_UNICAST: +- dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); +- break; ++ /* QCA qca-mcs support - Start */ ++ pdst = __br_get(get_dst_hook, NULL, p, &skb); ++ if (pdst) { ++ if (!skb) ++ goto out; ++ } else { ++ /* QCA qca-mcs support - End */ ++ dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); ++ } + default: + break; + } +@@ -189,6 +218,13 @@ int br_handle_frame_finish(struct net *n + dst->used = now; + br_forward(dst->dst, skb, local_rcv, false); + } else { ++ /* QCA qca-mcs support - Start */ ++ if (pdst) { ++ br_forward(pdst, skb, local_rcv, false); ++ goto out; ++ } ++ /* QCA qca-mcs support - End */ ++ + if (!mcast_hit) + br_flood(br, skb, pkt_type, local_rcv, false); + else +--- a/include/linux/mroute.h ++++ b/include/linux/mroute.h +@@ -85,4 +85,44 @@ struct rtmsg; + int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, u32 portid); ++ ++/* QCA ECM qca-mcs support - Start */ ++#define IPMR_MFC_EVENT_UPDATE 1 ++#define IPMR_MFC_EVENT_DELETE 2 ++ ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ipmr_mfc_event_offload_callback_t)(__be32 origin, __be32 group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ u8 op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur to ++ * MFC. The callback is registered by offload modules ++ */ ++extern bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ipmr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list, given a multicast group and source ++ */ ++extern int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dst_cnt, u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out); ++/* QCA ECM qca-mcs support - End */ + #endif +--- a/include/linux/mroute6.h ++++ b/include/linux/mroute6.h +@@ -110,4 +110,47 @@ static inline int ip6mr_sk_done(struct s + return 0; + } + #endif ++ ++/* QCA qca-mcs support - Start */ ++#define IP6MR_MFC_EVENT_UPDATE 1 ++#define IP6MR_MFC_EVENT_DELETE 2 ++ ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ip6mr_mfc_event_offload_callback_t)(struct in6_addr *origin, ++ struct in6_addr *group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ uint8_t op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur ++ * to MFC. The callback is registered by offload modules ++ */ ++extern bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ip6mr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list given a multicast group and source ++ */ ++extern int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dst_cnt, ++ u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, uint64_t pkts_in, ++ uint64_t bytes_in, uint64_t pkts_out, ++ uint64_t bytes_out); ++/* QCA qca-mcs support - End */ + #endif +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -89,6 +89,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* spinlock for offload */ ++static DEFINE_SPINLOCK(lock); /* QCA ECM qca-mcs support */ ++ + /* We return to original Alan's scheme. Hash table of resolved + * entries is changed only in process context and protected + * with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -112,6 +115,9 @@ static void mroute_netlink_event(struct + static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, __be32 origin, ++ __be32 mcastgrp); ++static ipmr_mfc_event_offload_callback_t __rcu ipmr_mfc_event_offload_callback; /* QCA ECM qca-mcs support */ + + #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES + #define ipmr_for_each_table(mrt, net) \ +@@ -223,6 +229,80 @@ static int ipmr_rule_fill(struct fib_rul + return 0; + } + ++/* QCA ECM qca-mcs support - Start */ ++/* ipmr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ipmr_sync_entry_update(struct mr_table *mrt, ++ struct mfc_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXVIFS]; ++ __be32 origin; ++ __be32 group; ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ origin = cache->mfc_origin; ++ group = cache->mfc_mcastgrp; ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ if (dest_if_count == MAXVIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, dest_if_count, dest_dev, ++ IPMR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ipmr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ipmr_sync_entry_delete(u32 origin, u32 group) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, 0, NULL, IPMR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++/* QCA ECM qca-mcs support - End */ ++ + static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { + .family = RTNL_FAMILY_IPMR, + .rule_size = sizeof(struct ipmr_rule), +@@ -236,6 +316,156 @@ static const struct fib_rules_ops __net_ + .owner = THIS_MODULE, + }; + ++/* QCA ECM qca-mcs support - Start */ ++/* ipmr_register_mfc_event_offload_callback() ++ * Register the IPv4 Multicast update offload callback with IPMR ++ */ ++bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ipmr_register_mfc_event_offload_callback); ++ ++/* ipmr_unregister_mfc_event_offload_callback() ++ * De-register the IPv4 Multicast update offload callback with IPMR ++ */ ++void ipmr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ipmr_unregister_mfc_event_offload_callback); ++ ++/* ipmr_find_mfc_entry() ++ * Returns destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dest_cnt, u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ipmr_find_mfc_entry); ++ ++/* ipmr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ spin_lock(&mrt_lock); ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ cache->_c.mfc_un.res.pkt += pkts_out; ++ cache->_c.mfc_un.res.bytes += bytes_out; ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ipmr_mfc_stats_update); ++/* QCA ECM qca-mcs support - End */ ++ + static int __net_init ipmr_rules_init(struct net *net) + { + struct fib_rules_ops *ops; +@@ -1191,6 +1421,10 @@ static int ipmr_mfc_delete(struct mr_tab + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); + mroute_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* QCA ECM qca-mcs support - Start */ ++ /* Inform offload modules of the delete event */ ++ ipmr_sync_entry_delete(c->mfc_origin, c->mfc_mcastgrp); ++ /* QCA ECM qca-mcs support - End */ + + return 0; + } +@@ -1221,6 +1455,10 @@ static int ipmr_mfc_add(struct net *net, + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, + mrt->id); + mroute_netlink_event(mrt, c, RTM_NEWROUTE); ++ /* QCA ECM qca-mcs support - Start */ ++ /* Inform offload modules of the update event */ ++ ipmr_sync_entry_update(mrt, c); ++ /* QCA ECM qca-mcs support - End */ + return 0; + } + +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -74,6 +74,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* Spinlock for offload */ ++static DEFINE_SPINLOCK(lock); /* QCA qca-mcs support */ ++ + /* We return to original Alan's scheme. Hash table of resolved + entries is changed only in process context and protected + with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -101,6 +104,13 @@ static int ip6mr_rtm_dumproute(struct sk + struct netlink_callback *cb); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++/* QCA qca-mcs support - Start */ ++static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt, ++ const struct in6_addr *origin, ++ const struct in6_addr *mcastgrp); ++static ip6mr_mfc_event_offload_callback_t __rcu ++ ip6mr_mfc_event_offload_callback; ++/* QCA qca-mcs support - End */ + + #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES + #define ip6mr_for_each_table(mrt, net) \ +@@ -375,6 +385,84 @@ static struct mfc6_cache_cmp_arg ip6mr_m + .mf6c_mcastgrp = IN6ADDR_ANY_INIT, + }; + ++/* QCA qca-mcs support - Start */ ++/* ip6mr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ip6mr_sync_entry_update(struct mr_table *mrt, ++ struct mfc6_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXMIFS]; ++ struct in6_addr mc_origin, mc_group; ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ spin_lock(&mrt_lock); ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ if (dest_if_count == MAXMIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ ++ memcpy(&mc_origin, &cache->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &cache->mf6c_mcastgrp, sizeof(struct in6_addr)); ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(&mc_group, &mc_origin, dest_if_count, dest_dev, ++ IP6MR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ip6mr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ip6mr_sync_entry_delete(struct in6_addr *mc_origin, ++ struct in6_addr *mc_group) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(mc_group, mc_origin, 0, NULL, ++ IP6MR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++/* QCA qca-mcs support - End */ ++ + static struct mr_table_ops ip6mr_mr_table_ops = { + .rht_params = &ip6mr_rht_params, + .cmparg_any = &ip6mr_mr_table_ops_cmparg_any, +@@ -697,6 +785,151 @@ static int call_ip6mr_mfc_entry_notifier + &mfc->_c, tb_id, &net->ipv6.ipmr_seq); + } + ++/* QCA qca-mcs support - Start */ ++/* ip6mr_register_mfc_event_offload_callback() ++ * Register the IPv6 multicast update callback for offload modules ++ */ ++bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ip6mr_register_mfc_event_offload_callback); ++ ++/* ip6mr_unregister_mfc_event_offload_callback() ++ * De-register the IPv6 multicast update callback for offload modules ++ */ ++void ip6mr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ip6mr_unregister_mfc_event_offload_callback); ++ ++/* ip6mr_find_mfc_entry() ++ * Return the destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dest_cnt, ++ u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ip6mr_find_mfc_entry); ++ ++/* ip6mr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u64 pkts_in, ++ u64 bytes_in, uint64_t pkts_out, ++ u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ cache->_c.mfc_un.res.pkt += pkts_out; ++ cache->_c.mfc_un.res.bytes += bytes_out; ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ ++ spin_unlock(&mrt_lock); ++ return 0; ++} ++EXPORT_SYMBOL(ip6mr_mfc_stats_update); ++/* QCA qca-mcs support - End */ ++ + /* Delete a VIF entry */ + static int mif6_delete(struct mr_table *mrt, int vifi, int notify, + struct list_head *head) +@@ -1221,6 +1454,7 @@ static int ip6mr_mfc_delete(struct mr_ta + int parent) + { + struct mfc6_cache *c; ++ struct in6_addr mc_origin, mc_group; /* QCA qca-mcs support */ + + /* The entries are added/deleted only under RTNL */ + rcu_read_lock(); +@@ -1229,6 +1463,11 @@ static int ip6mr_mfc_delete(struct mr_ta + rcu_read_unlock(); + if (!c) + return -ENOENT; ++ ++ /* QCA qca-mcs support - Start */ ++ memcpy(&mc_origin, &c->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &c->mf6c_mcastgrp, sizeof(struct in6_addr)); ++ /* QCA qca-mcs support - End */ + rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params); + list_del_rcu(&c->_c.list); + +@@ -1236,6 +1475,11 @@ static int ip6mr_mfc_delete(struct mr_ta + FIB_EVENT_ENTRY_DEL, c, mrt->id); + mr6_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* QCA qca-mcs support - Start */ ++ /* Inform offload modules of the delete event */ ++ ip6mr_sync_entry_delete(&mc_origin, &mc_group); ++ /* QCA qca-mcs support - End */ ++ + return 0; + } + +@@ -1457,6 +1701,10 @@ static int ip6mr_mfc_add(struct net *net + call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, + c, mrt->id); + mr6_netlink_event(mrt, c, RTM_NEWROUTE); ++ /* QCA qca-mcs support - Start */ ++ /* Inform offload modules of the update event */ ++ ip6mr_sync_entry_update(mrt, c); ++ /* QCA qca-mcs support - End */ + return 0; + } + diff --git a/target/linux/qualcommax/patches-6.1/0605-1-qca-nss-cfi-support.patch b/target/linux/qualcommax/patches-6.1/0605-1-qca-nss-cfi-support.patch new file mode 100644 index 00000000000000..67e85a18faec5c --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0605-1-qca-nss-cfi-support.patch @@ -0,0 +1,111 @@ +--- a/crypto/authenc.c ++++ b/crypto/authenc.c +@@ -417,6 +417,8 @@ static int crypto_authenc_create(struct + enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + ++ inst->alg.base.cra_flags |= (auth_base->cra_flags | ++ enc->base.cra_flags) & CRYPTO_ALG_NOSUPP_SG; + inst->alg.base.cra_priority = enc->base.cra_priority * 10 + + auth_base->cra_priority; + inst->alg.base.cra_blocksize = enc->base.cra_blocksize; +--- a/include/linux/crypto.h ++++ b/include/linux/crypto.h +@@ -101,6 +101,11 @@ + #define CRYPTO_NOLOAD 0x00008000 + + /* ++ * Set this flag if algorithm does not support SG list transforms ++ */ ++#define CRYPTO_ALG_NOSUPP_SG 0x0000c000 ++ ++/* + * The algorithm may allocate memory during request processing, i.e. during + * encryption, decryption, or hashing. Users can request an algorithm with this + * flag unset if they can't handle memory allocation failures. +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -658,6 +658,7 @@ static int esp_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -669,6 +670,11 @@ static int esp_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -890,6 +896,7 @@ static int esp_input(struct xfrm_state * + u8 *iv; + struct scatterlist *sg; + int err = -EINVAL; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) + goto out; +@@ -897,6 +904,12 @@ static int esp_input(struct xfrm_state * + if (elen <= 0) + goto out; + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -696,6 +696,7 @@ static int esp6_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -707,6 +708,11 @@ static int esp6_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -934,6 +940,7 @@ static int esp6_input(struct xfrm_state + __be32 *seqhi; + u8 *iv; + struct scatterlist *sg; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) { + ret = -EINVAL; +@@ -945,6 +952,12 @@ static int esp6_input(struct xfrm_state + goto out; + } + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + diff --git a/target/linux/qualcommax/patches-6.1/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch b/target/linux/qualcommax/patches-6.1/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch new file mode 100644 index 00000000000000..608ab7f2b33675 --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch @@ -0,0 +1,80 @@ +From eee3a7956b943dd3e23a74fbb5bfe89405eb0782 Mon Sep 17 00:00:00 2001 +From: Andrea Righi +Date: Mon, 6 Dec 2021 17:34:47 +0100 +Subject: UBUNTU: SAUCE: ipv6: fix NULL pointer dereference in ip6_output() + +It is possible to trigger a NULL pointer dereference by running the srv6 +net kselftest (tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh): + +[ 249.051216] BUG: kernel NULL pointer dereference, address: 0000000000000378 +[ 249.052331] #PF: supervisor read access in kernel mode +[ 249.053137] #PF: error_code(0x0000) - not-present page +[ 249.053960] PGD 0 P4D 0 +[ 249.054376] Oops: 0000 [#1] PREEMPT SMP NOPTI +[ 249.055083] CPU: 1 PID: 21 Comm: ksoftirqd/1 Tainted: G E 5.16.0-rc4 #2 +[ 249.056328] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 +[ 249.057632] RIP: 0010:ip6_forward+0x53c/0xab0 +[ 249.058354] Code: 49 c7 44 24 20 00 00 00 00 48 83 e0 fe 48 8b 40 30 48 3d 70 b2 b5 81 0f 85 b5 04 00 00 e8 7c f2 ff ff 41 89 c5 e9 17 01 00 00 <44> 8b 93 78 03 00 00 45 85 d2 0f 85 92 fb ff ff 49 8b 54 24 10 48 +[ 249.061274] RSP: 0018:ffffc900000cbb30 EFLAGS: 00010246 +[ 249.062042] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff8881051d3400 +[ 249.063141] RDX: ffff888104bda000 RSI: 00000000000002c0 RDI: 0000000000000000 +[ 249.064264] RBP: ffffc900000cbbc8 R08: 0000000000000000 R09: 0000000000000000 +[ 249.065376] R10: 0000000000000040 R11: 0000000000000000 R12: ffff888103409800 +[ 249.066498] R13: ffff8881051d3410 R14: ffff888102725280 R15: ffff888103525000 +[ 249.067619] FS: 0000000000000000(0000) GS:ffff88813bc80000(0000) knlGS:0000000000000000 +[ 249.068881] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 249.069777] CR2: 0000000000000378 CR3: 0000000104980000 CR4: 0000000000750ee0 +[ 249.070907] PKRU: 55555554 +[ 249.071337] Call Trace: +[ 249.071730] +[ 249.072070] ? debug_smp_processor_id+0x17/0x20 +[ 249.072807] seg6_input_core+0x2bb/0x2d0 +[ 249.073436] ? _raw_spin_unlock_irqrestore+0x29/0x40 +[ 249.074225] seg6_input+0x3b/0x130 +[ 249.074768] lwtunnel_input+0x5e/0xa0 +[ 249.075357] ip_rcv+0x17b/0x190 +[ 249.075867] ? update_load_avg+0x82/0x600 +[ 249.076514] __netif_receive_skb_one_core+0x86/0xa0 +[ 249.077231] __netif_receive_skb+0x15/0x60 +[ 249.077843] process_backlog+0x97/0x160 +[ 249.078389] __napi_poll+0x31/0x170 +[ 249.078912] net_rx_action+0x229/0x270 +[ 249.079506] __do_softirq+0xef/0x2ed +[ 249.080085] run_ksoftirqd+0x37/0x50 +[ 249.080663] smpboot_thread_fn+0x193/0x230 +[ 249.081312] kthread+0x17a/0x1a0 +[ 249.081847] ? smpboot_register_percpu_thread+0xe0/0xe0 +[ 249.082677] ? set_kthread_struct+0x50/0x50 +[ 249.083340] ret_from_fork+0x22/0x30 +[ 249.083926] +[ 249.090295] ---[ end trace 1998d7ba5965a365 ]--- + +It looks like commit 0857d6f8c759 ("ipv6: When forwarding count rx stats +on the orig netdev") tries to determine the right netdev to account the +rx stats, but in this particular case it's failing and the netdev is +NULL. + +Fallback to the previous method of determining the netdev interface (via +skb->dev) to account the rx stats when the orig netdev can't be +determined. + +Fixes: 0857d6f8c759 ("ipv6: When forwarding count rx stats on the orig netdev") +Signed-off-by: Andrea Righi +(cherry picked from https://lore.kernel.org/lkml/20211206163447.991402-1-andrea.righi@canonical.com/T/#u) +Signed-off-by: Andrea Righi +--- + net/ipv6/ip6_output.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -492,6 +492,9 @@ int ip6_forward(struct sk_buff *skb) + u32 mtu; + + idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); ++ if (unlikely(!idev)) ++ idev = __in6_dev_get_safely(skb->dev); ++ + if (net->ipv6.devconf_all->forwarding == 0) + goto error; + diff --git a/target/linux/qualcommax/patches-6.1/9990-1-qca-skb_recycler-support.patch b/target/linux/qualcommax/patches-6.1/9990-1-qca-skb_recycler-support.patch new file mode 100644 index 00000000000000..899e0a2b527f7b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/9990-1-qca-skb_recycler-support.patch @@ -0,0 +1,412 @@ +--- a/include/linux/cpuhotplug.h ++++ b/include/linux/cpuhotplug.h +@@ -94,6 +94,7 @@ enum cpuhp_state { + CPUHP_RADIX_DEAD, + CPUHP_PAGE_ALLOC, + CPUHP_NET_DEV_DEAD, ++ CPUHP_SKB_RECYCLER_DEAD, + CPUHP_PCI_XGENE_DEAD, + CPUHP_IOMMU_IOVA_DEAD, + CPUHP_LUSTRE_CFS_DEAD, +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -1240,7 +1240,7 @@ static inline void kfree_skb_list(struct sk_buff *segs) + kfree_skb_list_reason(segs, SKB_DROP_REASON_NOT_SPECIFIED); + } + +-#ifdef CONFIG_TRACEPOINTS ++#ifdef CONFIG_SKB_RECYCLER + void consume_skb(struct sk_buff *skb); + #else + static inline void consume_skb(struct sk_buff *skb) +@@ -1252,6 +1252,8 @@ static inline void consume_skb(struct sk_buff *skb) + void __consume_stateless_skb(struct sk_buff *skb); + void __kfree_skb(struct sk_buff *skb); + extern struct kmem_cache *skbuff_head_cache; ++extern void kfree_skbmem(struct sk_buff *skb); ++extern void skb_release_data(struct sk_buff *skb); + + void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); + bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -332,6 +332,27 @@ config NET_FLOW_LIMIT + with many clients some protection against DoS by a single (spoofed) + flow that greatly exceeds average workload. + ++config SKB_RECYCLER ++ bool "Generic skb recycling" ++ default y ++ help ++ SKB_RECYCLER is used to implement RX-to-RX skb recycling. ++ This config enables the recycling scheme for bridging and ++ routing workloads. It can reduce skbuff freeing or ++ reallocation overhead. ++ ++config SKB_RECYCLER_MULTI_CPU ++ bool "Cross-CPU recycling for CPU-locked workloads" ++ depends on SMP && SKB_RECYCLER ++ default n ++ ++config ALLOC_SKB_PAGE_FRAG_DISABLE ++ bool "Disable page fragment based skbuff payload allocations" ++ depends on !SKB_RECYCLER ++ default n ++ help ++ Disable page fragment based allocations for skbuff payloads. ++ + menu "Network testing" + + config NET_PKTGEN +--- a/net/core/Makefile ++++ b/net/core/Makefile +@@ -40,3 +40,4 @@ obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o + obj-$(CONFIG_BPF_SYSCALL) += sock_map.o + obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o + obj-$(CONFIG_OF) += of_net.o ++obj-$(CONFIG_SKB_RECYCLER) += skbuff_recycle.o +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -5974,10 +5974,16 @@ static int process_backlog(struct napi_struct *napi, int quota) + + napi->weight = READ_ONCE(dev_rx_weight); + while (again) { +- struct sk_buff *skb; ++ struct sk_buff *skb, *next_skb; + + while ((skb = __skb_dequeue(&sd->process_queue))) { + rcu_read_lock(); ++ ++ next_skb = skb_peek(&sd->process_queue); ++ if (likely(next_skb)) { ++ prefetch(next_skb->data); ++ } ++ + __netif_receive_skb(skb); + rcu_read_unlock(); + input_queue_head_incr(sd); +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -84,6 +84,33 @@ + #include "dev.h" + #include "sock_destructor.h" + ++struct kmem_cache *skb_data_cache; ++ ++/* ++ * For low memory profile, NSS_SKB_FIXED_SIZE_2K is enabled and ++ * CONFIG_SKB_RECYCLER is disabled. For premium and enterprise profile ++ * CONFIG_SKB_RECYCLER is enabled and NSS_SKB_FIXED_SIZE_2K is disabled. ++ * Irrespective of NSS_SKB_FIXED_SIZE_2K enabled/disabled, the ++ * CONFIG_SKB_RECYCLER and __LP64__ determines the value of SKB_DATA_CACHE_SIZE ++ */ ++#if defined(CONFIG_SKB_RECYCLER) ++/* ++ * 2688 for 64bit arch, 2624 for 32bit arch ++ */ ++#define SKB_DATA_CACHE_SIZE (SKB_DATA_ALIGN(SKB_RECYCLE_SIZE + NET_SKB_PAD) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#else ++/* ++ * 2368 for 64bit arch, 2176 for 32bit arch ++ */ ++#if defined(__LP64__) ++#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1984 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#else ++#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1856 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#endif ++#endif ++ ++#include "skbuff_recycle.h" ++ + struct kmem_cache *skbuff_head_cache __ro_after_init; + static struct kmem_cache *skbuff_fclone_cache __ro_after_init; + #ifdef CONFIG_SKB_EXTENSIONS +@@ -426,32 +453,46 @@ EXPORT_SYMBOL(napi_build_skb); + * memory is free + */ + static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, +- bool *pfmemalloc) +-{ +- bool ret_pfmemalloc = false; +- size_t obj_size; +- void *obj; ++ bool *pfmemalloc) ++ { ++ void *obj; ++ bool ret_pfmemalloc = false; ++ unsigned int obj_size = *size; ++ ++ if (obj_size > SZ_2K && obj_size <= SKB_DATA_CACHE_SIZE) { ++ obj = kmem_cache_alloc_node(skb_data_cache, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, ++ node); ++ *size = SKB_DATA_CACHE_SIZE; ++ if (obj || !(gfp_pfmemalloc_allowed(flags))) ++ goto out; + +- obj_size = SKB_HEAD_ALIGN(*size); ++ /* Try again but now we are using pfmemalloc reserves */ ++ ret_pfmemalloc = true; ++ obj = kmem_cache_alloc_node(skb_data_cache, flags, node); ++ goto out; ++ } + + obj_size = kmalloc_size_roundup(obj_size); +- /* The following cast might truncate high-order bits of obj_size, this ++ ++ /* ++ * The following cast might truncate high-order bits of obj_size, this + * is harmless because kmalloc(obj_size >= 2^32) will fail anyway. + */ + *size = (unsigned int)obj_size; + +- /* +- * Try a regular allocation, when that fails and we're not entitled +- * to the reserves, fail. +- */ ++ /* ++ * Try a regular allocation, when that fails and we're not entitled ++ * to the reserves, fail. ++ */ + obj = kmalloc_node_track_caller(obj_size, +- flags | __GFP_NOMEMALLOC | __GFP_NOWARN, +- node); +- if (obj || !(gfp_pfmemalloc_allowed(flags))) +- goto out; ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, ++ node); ++ if (obj || !(gfp_pfmemalloc_allowed(flags))) ++ goto out; + +- /* Try again but now we are using pfmemalloc reserves */ +- ret_pfmemalloc = true; ++ /* Try again but now we are using pfmemalloc reserves */ ++ ret_pfmemalloc = true; + obj = kmalloc_node_track_caller(obj_size, flags, node); + + out: +@@ -513,10 +554,12 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, + * aligned memory blocks, unless SLUB/SLAB debug is enabled. + * Both skb->head and skb_shared_info are cache line aligned. + */ ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, node, &pfmemalloc); + if (unlikely(!data)) + goto nodata; +- /* kmalloc_size_roundup() might give us more room than requested. ++ /* kmalloc_reserve(size) might give us more room than requested. + * Put skb_shared_info exactly at the end of allocated zone, + * to allow max possible filling before reallocation. + */ +@@ -551,7 +594,7 @@ EXPORT_SYMBOL(__alloc_skb); + /** + * __netdev_alloc_skb - allocate an skbuff for rx on a specific device + * @dev: network device to receive on +- * @len: length to allocate ++ * @length: length to allocate + * @gfp_mask: get_free_pages mask, passed to alloc_skb + * + * Allocate a new &sk_buff and assign it a usage count of one. The +@@ -561,29 +604,53 @@ EXPORT_SYMBOL(__alloc_skb); + * + * %NULL is returned if there is no free memory. + */ +-struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, +- gfp_t gfp_mask) ++struct sk_buff *__netdev_alloc_skb(struct net_device *dev, ++ unsigned int length, gfp_t gfp_mask) + { +- struct page_frag_cache *nc; + struct sk_buff *skb; ++ unsigned int len = length; ++ ++#ifdef CONFIG_SKB_RECYCLER ++ skb = skb_recycler_alloc(dev, length); ++ if (likely(skb)) ++ return skb; ++ ++ len = SKB_RECYCLE_SIZE; ++ if (unlikely(length > SKB_RECYCLE_SIZE)) ++ len = length; ++ ++ skb = __alloc_skb(len + NET_SKB_PAD, gfp_mask, ++ SKB_ALLOC_RX, NUMA_NO_NODE); ++ if (!skb) ++ goto skb_fail; ++ goto skb_success; ++#else ++ struct page_frag_cache *nc; + bool pfmemalloc; ++ bool page_frag_alloc_enable = true; + void *data; + + len += NET_SKB_PAD; + ++ ++#ifdef CONFIG_ALLOC_SKB_PAGE_FRAG_DISABLE ++ page_frag_alloc_enable = false; ++#endif + /* If requested length is either too small or too big, + * we use kmalloc() for skb->head allocation. + */ + if (len <= SKB_WITH_OVERHEAD(1024) || + len > SKB_WITH_OVERHEAD(PAGE_SIZE) || +- (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) { ++ (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA)) || ++ !page_frag_alloc_enable) { + skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE); + if (!skb) + goto skb_fail; + goto skb_success; + } + +- len = SKB_HEAD_ALIGN(len); ++ len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ len = SKB_DATA_ALIGN(len); + + if (sk_memalloc_socks()) + gfp_mask |= __GFP_MEMALLOC; +@@ -612,6 +679,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, + if (pfmemalloc) + skb->pfmemalloc = 1; + skb->head_frag = 1; ++#endif + + skb_success: + skb_reserve(skb, NET_SKB_PAD); +@@ -682,7 +750,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, + data = page_frag_alloc_1k(&nc->page_small, gfp_mask); + pfmemalloc = NAPI_SMALL_PAGE_PFMEMALLOC(nc->page_small); + } else { +- len = SKB_HEAD_ALIGN(len); ++ len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ len = SKB_DATA_ALIGN(len); + + data = page_frag_alloc(&nc->page, len, gfp_mask); + pfmemalloc = nc->page.pfmemalloc; +@@ -780,7 +849,7 @@ static void skb_free_head(struct sk_buff *skb) + } + } + +-static void skb_release_data(struct sk_buff *skb) ++void skb_release_data(struct sk_buff *skb) + { + struct skb_shared_info *shinfo = skb_shinfo(skb); + int i; +@@ -822,7 +891,7 @@ static void skb_release_data(struct sk_buff *skb) + /* + * Free an skbuff by memory without cleaning the state. + */ +-static void kfree_skbmem(struct sk_buff *skb) ++void kfree_skbmem(struct sk_buff *skb) + { + struct sk_buff_fclones *fclones; + +@@ -1034,7 +1103,6 @@ void skb_tx_error(struct sk_buff *skb) + } + EXPORT_SYMBOL(skb_tx_error); + +-#ifdef CONFIG_TRACEPOINTS + /** + * consume_skb - free an skbuff + * @skb: buffer to free +@@ -1043,13 +1111,50 @@ EXPORT_SYMBOL(skb_tx_error); + * Functions identically to kfree_skb, but kfree_skb assumes that the frame + * is being dropped after a failure and notes that + */ ++#ifdef CONFIG_SKB_RECYCLER + void consume_skb(struct sk_buff *skb) + { + if (!skb_unref(skb)) + return; + ++ prefetch(&skb->destructor); ++ ++ /*Tian: Not sure if we need to continue using this since ++ * since unref does the work in 5.4 ++ */ ++ ++ /* ++ if (likely(atomic_read(&skb->users) == 1)) ++ smp_rmb(); ++ else if (likely(!atomic_dec_and_test(&skb->users))) ++ return; ++ */ ++ ++ /* If possible we'd like to recycle any skb rather than just free it, ++ * but in order to do that we need to release any head state too. ++ * We don't want to do this later because we'll be in a pre-emption ++ * disabled state. ++ */ ++ skb_release_head_state(skb); ++ ++ /* Can we recycle this skb? If we can then it will be much faster ++ * for us to recycle this one later than to allocate a new one ++ * from scratch. ++ */ ++ if (likely(skb->head) && likely(skb_recycler_consume(skb))) ++ return; ++ ++#ifdef CONFIG_TRACEPOINTS + trace_consume_skb(skb); +- __kfree_skb(skb); ++#endif ++ /* We're not recycling so now we need to do the rest of what we would ++ * have done in __kfree_skb (above and beyond the skb_release_head_state ++ * that we already did). ++ */ ++ if (likely(skb->head)) ++ skb_release_data(skb); ++ ++ kfree_skbmem(skb); + } + EXPORT_SYMBOL(consume_skb); + #endif +@@ -1856,6 +1961,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + goto nodata; +@@ -4557,6 +4664,11 @@ static void skb_extensions_init(void) {} + + void __init skb_init(void) + { ++ skb_data_cache = kmem_cache_create_usercopy("skb_data_cache", ++ SKB_DATA_CACHE_SIZE, ++ 0, SLAB_PANIC, 0, SKB_DATA_CACHE_SIZE, ++ NULL); ++ + skbuff_head_cache = kmem_cache_create_usercopy("skbuff_head_cache", + sizeof(struct sk_buff), + 0, +@@ -4570,6 +4682,7 @@ void __init skb_init(void) + SLAB_HWCACHE_ALIGN|SLAB_PANIC, + NULL); + skb_extensions_init(); ++ skb_recycler_init(); + } + + static int +@@ -6224,6 +6337,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + return -ENOMEM; +@@ -6340,6 +6455,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + return -ENOMEM; diff --git a/target/linux/qualcommax/patches-6.1/9999-revert-crypto-api-disallow-identical-driver-names.patch b/target/linux/qualcommax/patches-6.1/9999-revert-crypto-api-disallow-identical-driver-names.patch new file mode 100644 index 00000000000000..0f4862e7fa287c --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/9999-revert-crypto-api-disallow-identical-driver-names.patch @@ -0,0 +1,10 @@ +--- a/crypto/algapi.c ++++ b/crypto/algapi.c +@@ -290,7 +290,6 @@ static struct crypto_larval *__crypto_re + } + + if (!strcmp(q->cra_driver_name, alg->cra_name) || +- !strcmp(q->cra_driver_name, alg->cra_driver_name) || + !strcmp(q->cra_name, alg->cra_driver_name)) + goto err; + } diff --git a/target/linux/qualcommax/patches-6.1/9999-silence-UBI-NAND-warnings.patch b/target/linux/qualcommax/patches-6.1/9999-silence-UBI-NAND-warnings.patch new file mode 100644 index 00000000000000..2eb85cb7b1700b --- /dev/null +++ b/target/linux/qualcommax/patches-6.1/9999-silence-UBI-NAND-warnings.patch @@ -0,0 +1,13 @@ +--- a/drivers/mtd/mtdblock.c ++++ b/drivers/mtd/mtdblock.c +@@ -261,10 +261,6 @@ static int mtdblock_open(struct mtd_blkt + return 0; + } + +- if (mtd_type_is_nand(mbd->mtd)) +- pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", +- mbd->tr->name, mbd->mtd->name); +- + /* OK, it's not open. Create cache info for it */ + mtdblk->count = 1; + mutex_init(&mtdblk->cache_mutex); diff --git a/target/linux/qualcommax/patches-6.6/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch b/target/linux/qualcommax/patches-6.6/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch index 6d97641f658cc1..64f8da87aadedb 100644 --- a/target/linux/qualcommax/patches-6.6/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch +++ b/target/linux/qualcommax/patches-6.6/0102-arm64-dts-ipq8074-add-reserved-memory-nodes.patch @@ -22,8 +22,8 @@ Signed-off-by: Robert Marko @@ -86,6 +86,16 @@ #size-cells = <2>; ranges; - -+ nss@40000000 { + ++ nss_region: nss@40000000 { + no-map; + reg = <0x0 0x40000000 0x0 0x01000000>; + }; @@ -56,5 +56,5 @@ Signed-off-by: Robert Marko + reg = <0x0 0x51000000 0x0 0x100000>; + }; }; - + firmware { diff --git a/target/linux/qualcommax/patches-6.6/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch b/target/linux/qualcommax/patches-6.6/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch new file mode 100644 index 00000000000000..76cc8caac9a412 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0170-clk-qcom-ipq8074-Support-added-for-necessary-clocks-and-reset.patch @@ -0,0 +1,311 @@ +From 6504bc9edeb1a2a54d813f4bb5d0267e7bf827f9 Mon Sep 17 00:00:00 2001 +From: Praveenkumar I +Date: Thu, 6 Feb 2020 17:35:42 +0530 +Subject: [PATCH 4/8] clk: ipq8074: Support added for necessary clocks and + reset + +Change-Id: I21a76a44185f766e9b6dcba274392ea8e599718b +Signed-off-by: Praveenkumar I +Signed-off-by: Rajkumar Ayyasamy +--- + drivers/clk/qcom/gcc-ipq8074.c | 238 ++++++++++++++++++- + include/dt-bindings/clock/qcom,gcc-ipq8074.h | 35 ++- + 2 files changed, 258 insertions(+), 15 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -48,6 +48,22 @@ enum { + P_UNIPHY2_TX, + }; + ++static const char * const gcc_xo_gpll4_gpll0_gpll6_gpll0_div2[] = { ++ "xo", ++ "gpll4", ++ "gpll0", ++ "gpll6", ++ "gpll0_out_main_div2", ++}; ++ ++static const struct parent_map gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map[] = { ++ { P_XO, 0 }, ++ { P_GPLL4, 1 }, ++ { P_GPLL0, 2 }, ++ { P_GPLL6, 3 }, ++ { P_GPLL0_DIV2, 4 }, ++}; ++ + static struct clk_alpha_pll gpll0_main = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], +@@ -629,6 +645,12 @@ static const struct freq_tbl ftbl_pcie_a + { } + }; + ++struct freq_tbl ftbl_pcie_rchng_clk_src[] = { ++ F(19200000, P_XO, 1, 0, 0), ++ F(100000000, P_GPLL0, 8, 0, 0), ++ { } ++}; ++ + static struct clk_rcg2 pcie0_axi_clk_src = { + .cmd_rcgr = 0x75054, + .freq_tbl = ftbl_pcie_axi_clk_src, +@@ -2029,6 +2051,78 @@ static struct clk_rcg2 gp3_clk_src = { + }, + }; + ++struct freq_tbl ftbl_qdss_tsctr_clk_src[] = { ++ F(160000000, P_GPLL0_DIV2, 2.5, 0, 0), ++ F(320000000, P_GPLL0, 2.5, 0, 0), ++ F(600000000, P_GPLL6, 2, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 qdss_tsctr_clk_src = { ++ .cmd_rcgr = 0x29064, ++ .freq_tbl = ftbl_qdss_tsctr_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "qdss_tsctr_clk_src", ++ .parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2, ++ .num_parents = 5, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ ++static struct clk_fixed_factor qdss_dap_sync_clk_src = { ++ .mult = 1, ++ .div = 4, ++ .hw.init = &(struct clk_init_data){ ++ .name = "qdss_dap_sync_clk_src", ++ .parent_names = (const char *[]){ ++ "qdss_tsctr_clk_src" ++ }, ++ .num_parents = 1, ++ .ops = &clk_fixed_factor_ops, ++ }, ++}; ++ ++struct freq_tbl ftbl_qdss_at_clk_src[] = { ++ F(66670000, P_GPLL0_DIV2, 6, 0, 0), ++ F(240000000, P_GPLL6, 6, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 qdss_at_clk_src = { ++ .cmd_rcgr = 0x2900c, ++ .freq_tbl = ftbl_qdss_at_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "qdss_at_clk_src", ++ .parent_names = gcc_xo_gpll4_gpll0_gpll6_gpll0_div2, ++ .num_parents = 5, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ ++ ++struct freq_tbl ftbl_adss_pwm_clk_src[] = { ++ F(19200000, P_XO, 1, 0, 0), ++ F(200000000, P_GPLL0, 4, 0, 0), ++ { } ++}; ++ ++struct clk_rcg2 adss_pwm_clk_src = { ++ .cmd_rcgr = 0x1c008, ++ .freq_tbl = ftbl_adss_pwm_clk_src, ++ .hid_width = 5, ++ .parent_map = gcc_xo_gpll0_map, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "adss_pwm_clk_src", ++ .parent_data = gcc_xo_gpll0, ++ .num_parents = 2, ++ .ops = &clk_rcg2_ops, ++ }, ++}; ++ + static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x01008, + .clkr = { +@@ -4224,13 +4318,7 @@ static struct clk_branch gcc_gp3_clk = { + }, + }; + +-static const struct freq_tbl ftbl_pcie_rchng_clk_src[] = { +- F(19200000, P_XO, 1, 0, 0), +- F(100000000, P_GPLL0, 8, 0, 0), +- { } +-}; +- +-static struct clk_rcg2 pcie0_rchng_clk_src = { ++struct clk_rcg2 pcie0_rchng_clk_src = { + .cmd_rcgr = 0x75070, + .freq_tbl = ftbl_pcie_rchng_clk_src, + .hid_width = 5, +@@ -4322,6 +4410,114 @@ static const struct alpha_pll_config nss + .alpha_en_mask = BIT(24), + }; + ++static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = { ++ .halt_reg = 0x4700c, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x4700c, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_snoc_bus_timeout2_ahb_clk", ++ .parent_names = (const char *[]){ ++ "usb0_master_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = { ++ .halt_reg = 0x47014, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x47014, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_snoc_bus_timeout3_ahb_clk", ++ .parent_names = (const char *[]){ ++ "usb1_master_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_dcc_clk = { ++ .halt_reg = 0x77004, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x77004, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_dcc_clk", ++ .parent_names = (const char *[]){ ++ "pcnoc_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_qdss_at_clk = { ++ .halt_reg = 0x29024, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x29024, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_qdss_at_clk", ++ .parent_names = (const char *[]){ ++ "qdss_at_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_qdss_dap_clk = { ++ .halt_reg = 0x29084, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x29084, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_qdss_dap_clk", ++ .parent_names = (const char *[]){ ++ "qdss_dap_sync_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gcc_adss_pwm_clk = { ++ .halt_reg = 0x1c020, ++ .halt_bit = 31, ++ .clkr = { ++ .enable_reg = 0x1c020, ++ .enable_mask = BIT(0), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gcc_adss_pwm_clk", ++ .parent_names = (const char *[]){ ++ "adss_pwm_clk_src" ++ }, ++ .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, ++ .ops = &clk_branch2_ops, ++ }, ++ }, ++}; ++ + static struct clk_hw *gcc_ipq8074_hws[] = { + &gpll0_out_main_div2.hw, + &gpll6_out_main_div2.hw, +@@ -4330,6 +4526,7 @@ static struct clk_hw *gcc_ipq8074_hws[] + &gcc_xo_div4_clk_src.hw, + &nss_noc_clk_src.hw, + &nss_ppe_cdiv_clk_src.hw, ++ &qdss_dap_sync_clk_src.hw, + }; + + static struct clk_regmap *gcc_ipq8074_clks[] = { +@@ -4561,6 +4758,15 @@ static struct clk_regmap *gcc_ipq8074_cl + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_CRYPTO_PPE_CLK] = &gcc_crypto_ppe_clk.clkr, ++ [GCC_SNOC_BUS_TIMEOUT2_AHB_CLK] = &gcc_snoc_bus_timeout2_ahb_clk.clkr, ++ [GCC_SNOC_BUS_TIMEOUT3_AHB_CLK] = &gcc_snoc_bus_timeout3_ahb_clk.clkr, ++ [GCC_DCC_CLK] = &gcc_dcc_clk.clkr, ++ [QDSS_TSCTR_CLK_SRC] = &qdss_tsctr_clk_src.clkr, ++ [QDSS_AT_CLK_SRC] = &qdss_at_clk_src.clkr, ++ [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr, ++ [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr, ++ [ADSS_PWM_CLK_SRC] = &adss_pwm_clk_src.clkr, ++ [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr, + }; + + static const struct qcom_reset_map gcc_ipq8074_resets[] = { +--- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h +@@ -230,10 +230,19 @@ + #define GCC_GP1_CLK 221 + #define GCC_GP2_CLK 222 + #define GCC_GP3_CLK 223 +-#define GCC_PCIE0_AXI_S_BRIDGE_CLK 224 +-#define GCC_PCIE0_RCHNG_CLK_SRC 225 +-#define GCC_PCIE0_RCHNG_CLK 226 +-#define GCC_CRYPTO_PPE_CLK 227 ++#define GCC_CRYPTO_PPE_CLK 224 ++#define GCC_PCIE0_RCHNG_CLK_SRC 225 ++#define GCC_PCIE0_RCHNG_CLK 226 ++#define GCC_PCIE0_AXI_S_BRIDGE_CLK 227 ++#define GCC_SNOC_BUS_TIMEOUT2_AHB_CLK 228 ++#define GCC_SNOC_BUS_TIMEOUT3_AHB_CLK 229 ++#define GCC_DCC_CLK 230 ++#define ADSS_PWM_CLK_SRC 231 ++#define GCC_ADSS_PWM_CLK 232 ++#define QDSS_TSCTR_CLK_SRC 233 ++#define QDSS_AT_CLK_SRC 234 ++#define GCC_QDSS_AT_CLK 235 ++#define GCC_QDSS_DAP_CLK 236 + + #define GCC_BLSP1_BCR 0 + #define GCC_BLSP1_QUP1_BCR 1 diff --git a/target/linux/qualcommax/patches-6.6/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch b/target/linux/qualcommax/patches-6.6/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch new file mode 100644 index 00000000000000..b1393fc9ad43c2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0171-1-clk-qcom-ipq8074-Fix-gcc_snoc_bus_timeout_ahb_clk-offset.patch @@ -0,0 +1,44 @@ +From 462aa0c53397ec5bf78e3e7f68aa8a3ca300f4ba Mon Sep 17 00:00:00 2001 +From: Selvam Sathappan Periakaruppan +Date: Tue, 24 Mar 2020 19:09:38 +0530 +Subject: [PATCH 5/8] clk: qcom: ipq8074: Fix gcc_snoc_bus_timeout_ahb_clk + offset + +By default, the ipq8074 V2 clks are provided in the gcc driver. +Updating the gcc_snoc_bus_timeout_ahb_clk offsets also as needed +in ipq8074 V2. + +Change-Id: I5a6e98d002f5c3354a804e55dd9ebb1f83f7f974 +Signed-off-by: Selvam Sathappan Periakaruppan +--- + drivers/clk/qcom/gcc-ipq8074.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -4411,10 +4411,10 @@ static const struct alpha_pll_config nss + }; + + static struct clk_branch gcc_snoc_bus_timeout2_ahb_clk = { +- .halt_reg = 0x4700c, ++ .halt_reg = 0x47014, + .halt_bit = 31, + .clkr = { +- .enable_reg = 0x4700c, ++ .enable_reg = 0x47014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_snoc_bus_timeout2_ahb_clk", +@@ -4429,10 +4429,10 @@ static struct clk_branch gcc_snoc_bus_ti + }; + + static struct clk_branch gcc_snoc_bus_timeout3_ahb_clk = { +- .halt_reg = 0x47014, ++ .halt_reg = 0x4701C, + .halt_bit = 31, + .clkr = { +- .enable_reg = 0x47014, ++ .enable_reg = 0x4701C, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_snoc_bus_timeout3_ahb_clk", diff --git a/target/linux/qualcommax/patches-6.6/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch b/target/linux/qualcommax/patches-6.6/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch new file mode 100644 index 00000000000000..a7abddd5fddf18 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0171-2-clk-qcom-ipq8074-Fix-gcc_blsp1_ahb_clk-properties.patch @@ -0,0 +1,41 @@ +From 52315bec6ed633b6a71f28b746029602f8bd70b9 Mon Sep 17 00:00:00 2001 +From: Balaji Prakash J +Date: Wed, 22 Apr 2020 20:35:30 +0530 +Subject: [PATCH] clk: ipq8074: fix gcc_blsp1_ahb_clk properties + +All the voting enabled clocks does not support the enable +from CBCR register. So, updated gcc_blsp1_ahb_clk enable +register and mask to enable bit in APCS_CLOCK_BRANCH_ENA_VOTE. + +Also, the voting controlled clocks are shared among multiple +components like APSS, RPM, NSS, TZ, etc. So, turning the +voting off from APSS does not make the clock off if it has +been voted from another component. Added the flag +BRANCH_HALT_VOTED in order to skip checking the clock +disable status. + +This change is referred from the below commits, +1. 246b4fb3af9bd65d8af794aac2f0e7b1ed9cc2dd +2. c8374157d5ae91d3b3e0d513d62808a798b32d3a + +Signed-off-by: Balaji Prakash J +Change-Id: I505cb560b31ad27a02c165fbe13bb33a2fc7d230 +--- + drivers/clk/qcom/gcc-ipq8074.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/drivers/clk/qcom/gcc-ipq8074.c ++++ b/drivers/clk/qcom/gcc-ipq8074.c +@@ -2125,9 +2125,10 @@ struct clk_rcg2 adss_pwm_clk_src = { + + static struct clk_branch gcc_blsp1_ahb_clk = { + .halt_reg = 0x01008, ++ .halt_check = BRANCH_HALT_VOTED, + .clkr = { +- .enable_reg = 0x01008, +- .enable_mask = BIT(0), ++ .enable_reg = 0x0b004, ++ .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_blsp1_ahb_clk", + .parent_hws = (const struct clk_hw *[]){ diff --git a/target/linux/qualcommax/patches-6.6/0600-1-qca-nss-ecm-support-CORE.patch b/target/linux/qualcommax/patches-6.6/0600-1-qca-nss-ecm-support-CORE.patch new file mode 100644 index 00000000000000..c49b9564044716 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-1-qca-nss-ecm-support-CORE.patch @@ -0,0 +1,875 @@ +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -71,6 +71,9 @@ void brioctl_set(int (*hook)(struct net + void __user *uarg)); + int br_ioctl_call(struct net *net, struct net_bridge *br, unsigned int cmd, + struct ifreq *ifr, void __user *uarg); ++extern void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats); ++extern bool br_is_hairpin_enabled(struct net_device *dev); + + #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) + int br_multicast_list_adjacent(struct net_device *dev, +@@ -213,4 +216,42 @@ static inline clock_t br_get_ageing_time + } + #endif + ++/* QCA NSS ECM support - Start */ ++extern struct net_device *br_port_dev_get(struct net_device *dev, ++ unsigned char *addr, ++ struct sk_buff *skb, ++ unsigned int cookie); ++extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr); ++extern void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid); ++extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, ++ const char *addr, ++ __u16 vid); ++extern void br_fdb_update_register_notify(struct notifier_block *nb); ++extern void br_fdb_update_unregister_notify(struct notifier_block *nb); ++ ++typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev, ++ struct sk_buff *skb, ++ unsigned char *addr, ++ unsigned int cookie); ++extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook; ++ ++#define BR_FDB_EVENT_ADD 0x01 ++#define BR_FDB_EVENT_DEL 0x02 ++ ++struct br_fdb_event { ++ struct net_device *dev; ++ unsigned char addr[6]; ++ unsigned char is_local; ++ struct net_bridge *br; ++ struct net_device *orig_dev; ++}; ++extern void br_fdb_register_notify(struct notifier_block *nb); ++extern void br_fdb_unregister_notify(struct notifier_block *nb); ++ ++typedef struct net_bridge_port *br_get_dst_hook_t( ++ const struct net_bridge_port *src, ++ struct sk_buff **skb); ++extern br_get_dst_hook_t __rcu *br_get_dst_hook; ++/* QCA NSS ECM support - End */ ++ + #endif +--- a/include/linux/if_vlan.h ++++ b/include/linux/if_vlan.h +@@ -143,7 +143,10 @@ extern struct net_device *__vlan_find_de + extern int vlan_for_each(struct net_device *dev, + int (*action)(struct net_device *dev, int vid, + void *arg), void *arg); ++extern void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats); /* QCA NSS ECM support */ + extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); ++extern struct net_device *vlan_dev_next_dev(const struct net_device *dev); /* QCA NSS ECM support */ + extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern __be16 vlan_dev_vlan_proto(const struct net_device *dev); + +@@ -236,6 +239,12 @@ extern void vlan_vids_del_by_dev(struct + extern bool vlan_uses_dev(const struct net_device *dev); + + #else ++static inline void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *stats) ++{ ++ ++} /* QCA NSS ECM support */ ++ + static inline struct net_device * + __vlan_find_dev_deep_rcu(struct net_device *real_dev, + __be16 vlan_proto, u16 vlan_id) +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -2936,6 +2936,10 @@ enum netdev_cmd { + NETDEV_OFFLOAD_XSTATS_REPORT_USED, + NETDEV_OFFLOAD_XSTATS_REPORT_DELTA, + NETDEV_XDP_FEAT_CHANGE, ++ /* QCA NSS ECM Support - Start */ ++ NETDEV_BR_JOIN, ++ NETDEV_BR_LEAVE, ++ /* QCA NSS ECM Support - End */ + }; + const char *netdev_cmd_to_name(enum netdev_cmd cmd); + +--- a/include/net/ip6_route.h ++++ b/include/net/ip6_route.h +@@ -207,6 +207,11 @@ void rt6_multipath_rebalance(struct fib6 + void rt6_uncached_list_add(struct rt6_info *rt); + void rt6_uncached_list_del(struct rt6_info *rt); + ++/* QCA NSS ECM support - Start */ ++int rt6_register_notifier(struct notifier_block *nb); ++int rt6_unregister_notifier(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) + { + const struct dst_entry *dst = skb_dst(skb); +--- a/include/net/neighbour.h ++++ b/include/net/neighbour.h +@@ -249,6 +249,13 @@ static inline int neigh_parms_family(str + return p->tbl->family; + } + ++/* QCA NSS ECM support - Start */ ++struct neigh_mac_update { ++ unsigned char old_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; ++ unsigned char update_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))]; ++}; ++/* QCA NSS ECM support - End */ ++ + #define NEIGH_PRIV_ALIGN sizeof(long long) + #define NEIGH_ENTRY_SIZE(size) ALIGN((size), NEIGH_PRIV_ALIGN) + +@@ -395,6 +402,11 @@ void __neigh_for_each_release(struct nei + int (*cb)(struct neighbour *)); + int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *); + ++/* QCA NSS ECM support - Start */ ++extern void neigh_mac_update_register_notify(struct notifier_block *nb); ++extern void neigh_mac_update_unregister_notify(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + struct neigh_seq_state { + struct seq_net_private p; + struct neigh_table *tbl; +@@ -600,4 +612,5 @@ static inline void neigh_update_is_route + *notify = 1; + } + } ++ + #endif +--- a/include/net/route.h ++++ b/include/net/route.h +@@ -237,6 +237,11 @@ struct rtable *rt_dst_alloc(struct net_d + unsigned int flags, u16 type, bool noxfrm); + struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt); + ++/* QCA NSS ECM support - Start */ ++int ip_rt_register_notifier(struct notifier_block *nb); ++int ip_rt_unregister_notifier(struct notifier_block *nb); ++/* QCA NSS ECM support - End */ ++ + struct in_ifaddr; + void fib_add_ifaddr(struct in_ifaddr *); + void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -2266,4 +2266,6 @@ 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)) /* QCA NSS ECM support */ + #endif +--- a/net/8021q/vlan_core.c ++++ b/net/8021q/vlan_core.c +@@ -72,6 +72,28 @@ bool vlan_do_receive(struct sk_buff **sk + return true; + } + ++/* QCA NSS ECM support - Start */ ++/* Update the VLAN device with statistics from network offload engines */ ++void __vlan_dev_update_accel_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct vlan_pcpu_stats *stats; ++ ++ if (!is_vlan_dev(dev)) ++ return; ++ ++ stats = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, 0); ++ ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&stats->syncp); ++} ++EXPORT_SYMBOL(__vlan_dev_update_accel_stats); ++/* QCA NSS ECM support - End */ ++ + /* Must be invoked with rcu_read_lock. */ + struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, + __be16 vlan_proto, u16 vlan_id) +@@ -110,6 +132,15 @@ struct net_device *vlan_dev_real_dev(con + } + EXPORT_SYMBOL(vlan_dev_real_dev); + ++/* QCA NSS ECM support - Start */ ++/* Caller is responsible to hold the reference of the returned device */ ++struct net_device *vlan_dev_next_dev(const struct net_device *dev) ++{ ++ return vlan_dev_priv(dev)->real_dev; ++} ++EXPORT_SYMBOL(vlan_dev_next_dev); ++/* QCA NSS ECM support - End */ ++ + u16 vlan_dev_vlan_id(const struct net_device *dev) + { + return vlan_dev_priv(dev)->vlan_id; +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -33,6 +33,20 @@ static const struct rhashtable_params br + + static struct kmem_cache *br_fdb_cache __read_mostly; + ++ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list); ++ ++void br_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_register_notify); ++ ++void br_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_unregister_notify); ++ + int __init br_fdb_init(void) + { + br_fdb_cache = kmem_cache_create("bridge_fdb_cache", +@@ -195,6 +209,25 @@ static void fdb_notify(struct net_bridge + if (swdev_notify) + br_switchdev_fdb_notify(br, fdb, type); + ++ /* QCA NSS ECM support - Start */ ++ if (fdb->dst) { ++ int event; ++ struct br_fdb_event fdb_event; ++ ++ if (type == RTM_NEWNEIGH) ++ event = BR_FDB_EVENT_ADD; ++ else ++ event = BR_FDB_EVENT_DEL; ++ ++ fdb_event.dev = fdb->dst->dev; ++ ether_addr_copy(fdb_event.addr, fdb->key.addr.addr); ++ fdb_event.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags); ++ atomic_notifier_call_chain(&br_fdb_notifier_list, ++ event, ++ (void *)&fdb_event); ++ } ++ /* QCA NSS ECM support - End */ ++ + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) + goto errout; +@@ -519,6 +552,22 @@ out: + spin_unlock_bh(&br->hash_lock); + } + ++/* QCA NSS ECM support - Start */ ++ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list); ++ ++void br_fdb_update_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_register_notify); ++ ++void br_fdb_update_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify); ++/* QCA NSS ECM support - End */ ++ + void br_fdb_cleanup(struct work_struct *work) + { + struct net_bridge *br = container_of(work, struct net_bridge, +@@ -527,6 +576,7 @@ void br_fdb_cleanup(struct work_struct * + unsigned long delay = hold_time(br); + unsigned long work_delay = delay; + unsigned long now = jiffies; ++ u8 mac_addr[6]; /* QCA NSS ECM support */ + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -553,8 +603,15 @@ void br_fdb_cleanup(struct work_struct * + work_delay = min(work_delay, this_timer - now); + } else { + spin_lock_bh(&br->hash_lock); +- if (!hlist_unhashed(&f->fdb_node)) ++ if (!hlist_unhashed(&f->fdb_node)) { ++ ether_addr_copy(mac_addr, f->key.addr.addr); + fdb_delete(br, f, true); ++ /* QCA NSS ECM support - Start */ ++ atomic_notifier_call_chain( ++ &br_fdb_update_notifier_list, 0, ++ (void *)mac_addr); ++ /* QCA NSS ECM support - End */ ++ } + spin_unlock_bh(&br->hash_lock); + } + } +@@ -891,6 +948,12 @@ void br_fdb_update(struct net_bridge *br + */ + if (unlikely(test_bit(BR_FDB_LOCKED, &fdb->flags))) + clear_bit(BR_FDB_LOCKED, &fdb->flags); ++ ++ /* QCA NSS ECM support - Start */ ++ atomic_notifier_call_chain( ++ &br_fdb_update_notifier_list, ++ 0, (void *)addr); ++ /* QCA NSS ECM support - End */ + } + + if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) +@@ -914,6 +977,64 @@ void br_fdb_update(struct net_bridge *br + } + } + ++/* QCA NSS ECM support - Start */ ++/* Refresh FDB entries for bridge packets being forwarded by offload engines */ ++void br_refresh_fdb_entry(struct net_device *dev, const char *addr) ++{ ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return; ++ ++ if (!is_valid_ether_addr(addr)) { ++ pr_info("bridge: Attempt to refresh with invalid ether address %pM\n", ++ addr); ++ return; ++ } ++ ++ rcu_read_lock(); ++ br_fdb_update(p->br, p, addr, 0, true); ++ rcu_read_unlock(); ++} ++EXPORT_SYMBOL_GPL(br_refresh_fdb_entry); ++ ++/* Update timestamp of FDB entries for bridge packets being forwarded by offload engines */ ++void br_fdb_entry_refresh(struct net_device *dev, const char *addr, __u16 vid) ++{ ++ struct net_bridge_fdb_entry *fdb; ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return; ++ ++ rcu_read_lock(); ++ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); ++ if (likely(fdb)) { ++ fdb->updated = jiffies; ++ } ++ rcu_read_unlock(); ++} ++EXPORT_SYMBOL_GPL(br_fdb_entry_refresh); ++ ++/* Look up the MAC address in the device's bridge fdb table */ ++struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev, ++ const char *addr, __u16 vid) ++{ ++ struct net_bridge_port *p = br_port_get_rcu(dev); ++ struct net_bridge_fdb_entry *fdb; ++ ++ if (!p || p->state == BR_STATE_DISABLED) ++ return NULL; ++ ++ rcu_read_lock(); ++ fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid); ++ rcu_read_unlock(); ++ ++ return fdb; ++} ++EXPORT_SYMBOL_GPL(br_fdb_has_entry); ++ ++/* QCA NSS ECM support - End */ + /* Dump information about entries, in response to GETNEIGH */ + int br_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -26,6 +26,12 @@ + + #include "br_private.h" + ++/* QCA NSS ECM support - Start */ ++/* Hook for external forwarding logic */ ++br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_port_dev_get_hook); ++/* QCA NSS ECM support - End */ ++ + /* + * Determine initial path cost based on speed. + * using recommendations from 802.1d standard +@@ -697,6 +703,8 @@ int br_add_if(struct net_bridge *br, str + + kobject_uevent(&p->kobj, KOBJ_ADD); + ++ call_netdevice_notifiers(NETDEV_BR_JOIN, dev); /* QCA NSS ECM support */ ++ + return 0; + + err6: +@@ -732,6 +740,8 @@ int br_del_if(struct net_bridge *br, str + if (!p || p->br != br) + return -EINVAL; + ++ call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); /* QCA NSS ECM support */ ++ + /* Since more than one interface can be attached to a bridge, + * there still maybe an alternate path for netconsole to use; + * therefore there is no reason for a NETDEV_RELEASE event. +@@ -775,3 +785,97 @@ bool br_port_flag_is_set(const struct ne + return p->flags & flag; + } + EXPORT_SYMBOL_GPL(br_port_flag_is_set); ++ ++/* br_port_dev_get() ++ * If a skb is provided, and the br_port_dev_get_hook_t hook exists, ++ * use that to try and determine the egress port for that skb. ++ * If not, or no egress port could be determined, use the given addr ++ * to identify the port to which it is reachable, ++ * returing a reference to the net device associated with that port. ++ * ++ * NOTE: Return NULL if given dev is not a bridge or the mac has no ++ * associated port. ++ */ ++struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr, ++ struct sk_buff *skb, ++ unsigned int cookie) ++{ ++ struct net_bridge_fdb_entry *fdbe; ++ struct net_bridge *br; ++ struct net_device *netdev = NULL; ++ ++ /* Is this a bridge? */ ++ if (!(dev->priv_flags & IFF_EBRIDGE)) ++ return NULL; ++ ++ rcu_read_lock(); ++ ++ /* If the hook exists and the skb isn't NULL, try and get the port */ ++ if (skb) { ++ br_port_dev_get_hook_t *port_dev_get_hook; ++ ++ port_dev_get_hook = rcu_dereference(br_port_dev_get_hook); ++ if (port_dev_get_hook) { ++ struct net_bridge_port *pdst = ++ __br_get(port_dev_get_hook, NULL, dev, skb, ++ addr, cookie); ++ if (pdst) { ++ dev_hold(pdst->dev); ++ netdev = pdst->dev; ++ goto out; ++ } ++ } ++ } ++ ++ /* Either there is no hook, or can't ++ * determine the port to use - fall back to using FDB ++ */ ++ ++ br = netdev_priv(dev); ++ ++ /* Lookup the fdb entry and get reference to the port dev */ ++ fdbe = br_fdb_find_rcu(br, addr, 0); ++ if (fdbe && fdbe->dst) { ++ netdev = fdbe->dst->dev; /* port device */ ++ dev_hold(netdev); ++ } ++out: ++ rcu_read_unlock(); ++ return netdev; ++} ++EXPORT_SYMBOL_GPL(br_port_dev_get); ++ ++/* Update bridge statistics for bridge packets processed by offload engines */ ++void br_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *nlstats) ++{ ++ struct pcpu_sw_netstats *tstats; ++ ++ /* Is this a bridge? */ ++ if (!(dev->priv_flags & IFF_EBRIDGE)) ++ return; ++ ++ tstats = this_cpu_ptr(dev->tstats); ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->rx_packets, nlstats->rx_packets); ++ u64_stats_add(&tstats->rx_bytes, nlstats->rx_bytes); ++ u64_stats_add(&tstats->tx_packets, nlstats->tx_packets); ++ u64_stats_add(&tstats->tx_bytes, nlstats->tx_bytes); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL_GPL(br_dev_update_stats); ++ ++/* QCA NSS ECM support - Start */ ++/* API to know if hairpin feature is enabled/disabled on this bridge port */ ++bool br_is_hairpin_enabled(struct net_device *dev) ++{ ++ struct net_bridge_port *port = br_port_get_check_rcu(dev); ++ ++ if (likely(port)) ++ return port->flags & BR_HAIRPIN_MODE; ++ return false; ++} ++EXPORT_SYMBOL_GPL(br_is_hairpin_enabled); ++ ++/* QCA NSS ECM support - End */ +--- a/net/core/neighbour.c ++++ b/net/core/neighbour.c +@@ -1275,6 +1275,22 @@ static void neigh_update_hhs(struct neig + } + } + ++/* QCA NSS ECM support - Start */ ++ATOMIC_NOTIFIER_HEAD(neigh_mac_update_notifier_list); ++ ++void neigh_mac_update_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&neigh_mac_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(neigh_mac_update_register_notify); ++ ++void neigh_mac_update_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&neigh_mac_update_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(neigh_mac_update_unregister_notify); ++/* QCA NSS ECM support - End */ ++ + /* Generic update routine. + -- lladdr is new lladdr or NULL, if it is not supplied. + -- new is new state. +@@ -1303,6 +1319,7 @@ static int __neigh_update(struct neighbo + struct net_device *dev; + int err, notify = 0; + u8 old; ++ struct neigh_mac_update nmu; /* QCA NSS ECM support */ + + trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); + +@@ -1317,7 +1334,10 @@ static int __neigh_update(struct neighbo + new = old; + goto out; + } +- if (!(flags & NEIGH_UPDATE_F_ADMIN) && ++ ++ memset(&nmu, 0, sizeof(struct neigh_mac_update)); /* QCA NSS ECM support */ ++ ++ if (!(flags & NEIGH_UPDATE_F_ADMIN) && + (old & (NUD_NOARP | NUD_PERMANENT))) + goto out; + +@@ -1354,7 +1374,12 @@ static int __neigh_update(struct neighbo + - compare new & old + - if they are different, check override flag + */ +- if ((old & NUD_VALID) && ++ /* QCA NSS ECM update - Start */ ++ memcpy(nmu.old_mac, neigh->ha, dev->addr_len); ++ memcpy(nmu.update_mac, lladdr, dev->addr_len); ++ /* QCA NSS ECM update - End */ ++ ++ if ((old & NUD_VALID) && + !memcmp(lladdr, neigh->ha, dev->addr_len)) + lladdr = neigh->ha; + } else { +@@ -1476,8 +1501,11 @@ out: + neigh_update_gc_list(neigh); + if (managed_update) + neigh_update_managed_list(neigh); +- if (notify) ++ if (notify) { + neigh_update_notify(neigh, nlmsg_pid); ++ atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0, ++ (struct neigh_mac_update *)&nmu); /* QCA NSS ECM support */ ++ } + trace_neigh_update_done(neigh, err); + return err; + } +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -1211,6 +1211,9 @@ static bool fib_valid_key_len(u32 key, u + static void fib_remove_alias(struct trie *t, struct key_vector *tp, + struct key_vector *l, struct fib_alias *old); + ++/* Define route change notification chain. */ ++static BLOCKING_NOTIFIER_HEAD(iproute_chain); /* QCA NSS ECM support */ ++ + /* Caller must hold RTNL. */ + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) +@@ -1404,6 +1407,9 @@ int fib_table_insert(struct net *net, st + rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, + &cfg->fc_nlinfo, nlflags); + succeeded: ++ blocking_notifier_call_chain(&iproute_chain, ++ RTM_NEWROUTE, fi); ++ + return 0; + + out_remove_new_fa: +@@ -1775,6 +1781,9 @@ int fib_table_delete(struct net *net, st + if (fa_to_delete->fa_state & FA_S_ACCESSED) + rt_cache_flush(cfg->fc_nlinfo.nl_net); + ++ blocking_notifier_call_chain(&iproute_chain, ++ RTM_DELROUTE, fa_to_delete->fa_info); ++ + fib_release_info(fa_to_delete->fa_info); + alias_free_mem_rcu(fa_to_delete); + return 0; +@@ -2407,6 +2416,20 @@ void __init fib_trie_init(void) + 0, SLAB_PANIC | SLAB_ACCOUNT, NULL); + } + ++/* QCA NSS ECM support - Start */ ++int ip_rt_register_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&iproute_chain, nb); ++} ++EXPORT_SYMBOL(ip_rt_register_notifier); ++ ++int ip_rt_unregister_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&iproute_chain, nb); ++} ++EXPORT_SYMBOL(ip_rt_unregister_notifier); ++/* QCA NSS ECM support - End */ ++ + struct fib_table *fib_trie_table(u32 id, struct fib_table *alias) + { + struct fib_table *tb; +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -666,6 +666,7 @@ void ndisc_send_ns(struct net_device *de + if (skb) + ndisc_send_skb(skb, daddr, saddr); + } ++EXPORT_SYMBOL(ndisc_send_ns); + + void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, + const struct in6_addr *daddr) +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -197,6 +197,9 @@ static void rt6_uncached_list_flush_dev( + } + } + ++/* Define route change notification chain. */ ++ATOMIC_NOTIFIER_HEAD(ip6route_chain); /* QCA NSS ECM support */ ++ + static inline const void *choose_neigh_daddr(const struct in6_addr *p, + struct sk_buff *skb, + const void *daddr) +@@ -3864,6 +3867,10 @@ int ip6_route_add(struct fib6_config *cf + return PTR_ERR(rt); + + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack); ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_NEWROUTE, rt); ++ + fib6_info_release(rt); + + return err; +@@ -3885,6 +3892,9 @@ static int __ip6_del_rt(struct fib6_info + err = fib6_del(rt, info); + spin_unlock_bh(&table->tb6_lock); + ++ if (!err) ++ atomic_notifier_call_chain(&ip6route_chain, ++ RTM_DELROUTE, rt); + out: + fib6_info_release(rt); + return err; +@@ -6329,6 +6339,20 @@ static int ip6_route_dev_notify(struct n + return NOTIFY_OK; + } + ++/* QCA NSS ECM support - Start */ ++int rt6_register_notifier(struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_register(&ip6route_chain, nb); ++} ++EXPORT_SYMBOL(rt6_register_notifier); ++ ++int rt6_unregister_notifier(struct notifier_block *nb) ++{ ++ return atomic_notifier_chain_unregister(&ip6route_chain, nb); ++} ++EXPORT_SYMBOL(rt6_unregister_notifier); ++/* QCA NSS ECM support - End */ ++ + /* + * /proc + */ +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -1673,6 +1673,7 @@ 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"; +--- a/net/ipv6/addrconf.c ++++ b/net/ipv6/addrconf.c +@@ -1002,6 +1002,7 @@ void inet6_ifa_finish_destroy(struct ine + + kfree_rcu(ifp, rcu); + } ++EXPORT_SYMBOL(inet6_ifa_finish_destroy); + + static void + ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -440,6 +440,15 @@ static inline __be32 vxlan_compute_rco(u + return vni_field; + } + ++/* ++ * vxlan_get_vni() ++ * Returns the vni corresponding to tunnel ++ */ ++static inline u32 vxlan_get_vni(struct vxlan_dev *vxlan_tun) ++{ ++ return be32_to_cpu(vxlan_tun->cfg.vni); ++} ++ + static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs) + { + return vs->sock->sk->sk_family; +--- a/include/uapi/linux/in.h ++++ b/include/uapi/linux/in.h +@@ -63,6 +63,8 @@ enum { + #define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ + #define IPPROTO_BEETPH IPPROTO_BEETPH ++ IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ ++#define IPPROTO_ETHERIP IPPROTO_ETHERIP + IPPROTO_ENCAP = 98, /* Encapsulation Header */ + #define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +@@ -327,7 +329,7 @@ struct sockaddr_in { + #endif + + /* contains the htonl type stuff.. */ +-#include ++#include + + + #endif /* _UAPI_LINUX_IN_H */ +--- a/tools/include/uapi/linux/in.h ++++ b/tools/include/uapi/linux/in.h +@@ -63,6 +63,8 @@ enum { + #define IPPROTO_MTP IPPROTO_MTP + IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */ + #define IPPROTO_BEETPH IPPROTO_BEETPH ++ IPPROTO_ETHERIP = 97, /* ETHERIP protocol number */ ++#define IPPROTO_ETHERIP IPPROTO_ETHERIP + IPPROTO_ENCAP = 98, /* Encapsulation Header */ + #define IPPROTO_ENCAP IPPROTO_ENCAP + IPPROTO_PIM = 103, /* Protocol Independent Multicast */ +@@ -327,7 +329,7 @@ struct sockaddr_in { + #endif + + /* contains the htonl type stuff.. */ +-#include ++#include + + + #endif /* _UAPI_LINUX_IN_H */ +--- a/net/netfilter/nf_conntrack_ecache.c ++++ b/net/netfilter/nf_conntrack_ecache.c +@@ -266,7 +266,6 @@ void nf_conntrack_register_notifier(stru + mutex_lock(&nf_ct_ecache_mutex); + notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb, + lockdep_is_held(&nf_ct_ecache_mutex)); +- WARN_ON_ONCE(notify); + rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new); + mutex_unlock(&nf_ct_ecache_mutex); + } +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -26,6 +26,7 @@ struct nf_tcp_net { + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + u8 tcp_loose; + u8 tcp_be_liberal; ++ u8 tcp_no_window_check; + u8 tcp_max_retrans; + u8 tcp_ignore_invalid_rst; + #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -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 +@@ -633,6 +633,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, +@@ -840,6 +841,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), +@@ -1050,6 +1059,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/qualcommax/patches-6.6/0600-2-qca-nss-ecm-support-PPPOE-offload.patch b/target/linux/qualcommax/patches-6.6/0600-2-qca-nss-ecm-support-PPPOE-offload.patch new file mode 100644 index 00000000000000..e59290878db29e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-2-qca-nss-ecm-support-PPPOE-offload.patch @@ -0,0 +1,600 @@ +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -48,6 +48,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -254,6 +255,25 @@ struct ppp_net { + #define seq_before(a, b) ((s32)((a) - (b)) < 0) + #define seq_after(a, b) ((s32)((a) - (b)) > 0) + ++ ++/* ++ * Registration/Unregistration methods ++ * for PPP channel connect and disconnect event notifications. ++ */ ++RAW_NOTIFIER_HEAD(ppp_channel_connection_notifier_list); ++ ++void ppp_channel_connection_register_notify(struct notifier_block *nb) ++{ ++ raw_notifier_chain_register(&ppp_channel_connection_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(ppp_channel_connection_register_notify); ++ ++void ppp_channel_connection_unregister_notify(struct notifier_block *nb) ++{ ++ raw_notifier_chain_unregister(&ppp_channel_connection_notifier_list, nb); ++} ++EXPORT_SYMBOL_GPL(ppp_channel_connection_unregister_notify); ++ + /* Prototypes. */ + static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, + struct file *file, unsigned int cmd, unsigned long arg); +@@ -3453,7 +3473,10 @@ ppp_connect_channel(struct channel *pch, + struct ppp_net *pn; + int ret = -ENXIO; + int hdrlen; ++ int ppp_proto; ++ int version; + ++ int notify = 0; + pn = ppp_pernet(pch->chan_net); + + mutex_lock(&pn->all_ppp_mutex); +@@ -3485,13 +3508,40 @@ ppp_connect_channel(struct channel *pch, + ++ppp->n_channels; + pch->ppp = ppp; + refcount_inc(&ppp->file.refcnt); ++ ++ /* Set the netdev priv flag if the prototype ++ * is L2TP or PPTP. Return success in all cases ++ */ ++ if (!pch->chan) ++ goto out2; ++ ++ ppp_proto = ppp_channel_get_protocol(pch->chan); ++ if (ppp_proto == PX_PROTO_PPTP) { ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_PPTP; ++ } else if (ppp_proto == PX_PROTO_OL2TP) { ++ version = ppp_channel_get_proto_version(pch->chan); ++ if (version == 2) ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV2; ++ else if (version == 3) ++ ppp->dev->priv_flags_ext |= IFF_EXT_PPP_L2TPV3; ++ } ++ notify = 1; ++ ++ out2: + ppp_unlock(ppp); + ret = 0; +- + outl: + write_unlock_bh(&pch->upl); + out: + mutex_unlock(&pn->all_ppp_mutex); ++ ++ if (notify && ppp && ppp->dev) { ++ dev_hold(ppp->dev); ++ raw_notifier_call_chain(&ppp_channel_connection_notifier_list, ++ PPP_CHANNEL_CONNECT, ppp->dev); ++ dev_put(ppp->dev); ++ } ++ + return ret; + } + +@@ -3509,6 +3559,13 @@ ppp_disconnect_channel(struct channel *p + pch->ppp = NULL; + write_unlock_bh(&pch->upl); + if (ppp) { ++ if (ppp->dev) { ++ dev_hold(ppp->dev); ++ raw_notifier_call_chain(&ppp_channel_connection_notifier_list, ++ PPP_CHANNEL_DISCONNECT, ppp->dev); ++ dev_put(ppp->dev); ++ } ++ + /* remove it from the ppp unit's list */ + ppp_lock(ppp); + list_del(&pch->clist); +@@ -3588,6 +3645,222 @@ static void *unit_find(struct idr *p, in + return idr_find(p, n); + } + ++/* Updates the PPP interface statistics. */ ++void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped) ++{ ++ struct ppp *ppp; ++ ++ if (!dev) ++ return; ++ ++ if (dev->type != ARPHRD_PPP) ++ return; ++ ++ ppp = netdev_priv(dev); ++ ++ ppp_xmit_lock(ppp); ++ ppp->stats64.tx_packets += tx_packets; ++ ppp->stats64.tx_bytes += tx_bytes; ++ ppp->dev->stats.tx_errors += tx_errors; ++ ppp->dev->stats.tx_dropped += tx_dropped; ++ if (tx_packets) ++ ppp->last_xmit = jiffies; ++ ppp_xmit_unlock(ppp); ++ ++ ppp_recv_lock(ppp); ++ ppp->stats64.rx_packets += rx_packets; ++ ppp->stats64.rx_bytes += rx_bytes; ++ ppp->dev->stats.rx_errors += rx_errors; ++ ppp->dev->stats.rx_dropped += rx_dropped; ++ if (rx_packets) ++ ppp->last_recv = jiffies; ++ ppp_recv_unlock(ppp); ++} ++ ++/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if ++ * the device is not PPP. ++ */ ++int ppp_is_multilink(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ unsigned int flags; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ppp_lock(ppp); ++ flags = ppp->flags; ++ ppp_unlock(ppp); ++ ++ if (flags & SC_MULTILINK) ++ return 1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(ppp_is_multilink); ++ ++/* ppp_channel_get_protocol() ++ * Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ * ++ * NOTE: Some channels do not use PX sockets so the protocol value may be very ++ * different for them. ++ * NOTE: -1 indicates failure. ++ * NOTE: Once you know the channel protocol you may then either cast 'chan' to ++ * its sub-class or use the channel protocol specific API's as provided by that ++ * channel sub type. ++ */ ++int ppp_channel_get_protocol(struct ppp_channel *chan) ++{ ++ if (!chan->ops->get_channel_protocol) ++ return -1; ++ ++ return chan->ops->get_channel_protocol(chan); ++} ++EXPORT_SYMBOL(ppp_channel_get_protocol); ++ ++/* ppp_channel_get_proto_version() ++ * Call this to get channel protocol version ++ */ ++int ppp_channel_get_proto_version(struct ppp_channel *chan) ++{ ++ if (!chan->ops->get_channel_protocol_ver) ++ return -1; ++ ++ return chan->ops->get_channel_protocol_ver(chan); ++} ++EXPORT_SYMBOL(ppp_channel_get_proto_version); ++ ++/* ppp_channel_hold() ++ * Call this to hold a channel. ++ * ++ * Returns true on success or false if the hold could not happen. ++ * ++ * NOTE: chan must be protected against destruction during this call - ++ * either by correct locking etc. or because you already have an implicit ++ * or explicit hold to the channel already and this is an additional hold. ++ */ ++bool ppp_channel_hold(struct ppp_channel *chan) ++{ ++ if (!chan->ops->hold) ++ return false; ++ ++ chan->ops->hold(chan); ++ return true; ++} ++EXPORT_SYMBOL(ppp_channel_hold); ++ ++/* ppp_channel_release() ++ * Call this to release a hold you have upon a channel ++ */ ++void ppp_channel_release(struct ppp_channel *chan) ++{ ++ chan->ops->release(chan); ++} ++EXPORT_SYMBOL(ppp_channel_release); ++ ++/* Check if ppp xmit lock is on hold */ ++bool ppp_is_xmit_locked(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ ++ if (!dev) ++ return false; ++ ++ if (dev->type != ARPHRD_PPP) ++ return false; ++ ++ ppp = netdev_priv(dev); ++ if (!ppp) ++ return false; ++ ++ if (spin_is_locked(&(ppp)->wlock)) ++ return true; ++ ++ return false; ++} ++EXPORT_SYMBOL(ppp_is_xmit_locked); ++ ++/* ppp_hold_channels() ++ * Returns the PPP channels of the PPP device, storing each one into ++ * channels[]. ++ * ++ * channels[] has chan_sz elements. ++ * This function returns the number of channels stored, up to chan_sz. ++ * It will return < 0 if the device is not PPP. ++ * ++ * You MUST release the channels using ppp_release_channels(). ++ */ ++int ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], ++ unsigned int chan_sz) ++{ ++ struct ppp *ppp; ++ int c; ++ struct channel *pch; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ++ c = 0; ++ ppp_lock(ppp); ++ list_for_each_entry(pch, &ppp->channels, clist) { ++ struct ppp_channel *chan; ++ ++ if (!pch->chan) { ++ /* Channel is going / gone away */ ++ continue; ++ } ++ ++ if (c == chan_sz) { ++ /* No space to record channel */ ++ ppp_unlock(ppp); ++ return c; ++ } ++ ++ /* Hold the channel, if supported */ ++ chan = pch->chan; ++ if (!chan->ops->hold) ++ continue; ++ ++ chan->ops->hold(chan); ++ ++ /* Record the channel */ ++ channels[c++] = chan; ++ } ++ ppp_unlock(ppp); ++ return c; ++} ++EXPORT_SYMBOL(ppp_hold_channels); ++ ++/* ppp_release_channels() ++ * Releases channels ++ */ ++void ppp_release_channels(struct ppp_channel *channels[], unsigned int chan_sz) ++{ ++ unsigned int c; ++ ++ for (c = 0; c < chan_sz; ++c) { ++ struct ppp_channel *chan; ++ ++ chan = channels[c]; ++ chan->ops->release(chan); ++ } ++} ++EXPORT_SYMBOL(ppp_release_channels); ++ + /* Module/initialization stuff */ + + module_init(ppp_init); +@@ -3604,6 +3877,7 @@ EXPORT_SYMBOL(ppp_input_error); + EXPORT_SYMBOL(ppp_output_wakeup); + EXPORT_SYMBOL(ppp_register_compressor); + EXPORT_SYMBOL(ppp_unregister_compressor); ++EXPORT_SYMBOL(ppp_update_stats); + MODULE_LICENSE("GPL"); + MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); + MODULE_ALIAS_RTNL_LINK("ppp"); +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -62,6 +62,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -87,7 +88,7 @@ + static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb); + + static const struct proto_ops pppoe_ops; +-static const struct ppp_channel_ops pppoe_chan_ops; ++static const struct pppoe_channel_ops pppoe_chan_ops; + + /* per-net private data for this module */ + static unsigned int pppoe_net_id __read_mostly; +@@ -692,7 +693,7 @@ static int pppoe_connect(struct socket * + + po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr) - 2; + po->chan.private = sk; +- po->chan.ops = &pppoe_chan_ops; ++ po->chan.ops = (struct ppp_channel_ops *)&pppoe_chan_ops; + + error = ppp_register_net_channel(dev_net(dev), &po->chan); + if (error) { +@@ -995,9 +996,80 @@ static int pppoe_fill_forward_path(struc + return 0; + } + +-static const struct ppp_channel_ops pppoe_chan_ops = { +- .start_xmit = pppoe_xmit, +- .fill_forward_path = pppoe_fill_forward_path, ++/************************************************************************ ++ * ++ * function called by generic PPP driver to hold channel ++ * ++ ***********************************************************************/ ++static void pppoe_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/************************************************************************ ++ * ++ * function called by generic PPP driver to release channel ++ * ++ ***********************************************************************/ ++static void pppoe_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/************************************************************************ ++ * ++ * function called to get the channel protocol type ++ * ++ ***********************************************************************/ ++static int pppoe_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_OE; ++} ++ ++/************************************************************************ ++ * ++ * function called to get the PPPoE channel addressing ++ * NOTE: This function returns a HOLD to the netdevice ++ * ++ ***********************************************************************/ ++static int pppoe_get_addressing(struct ppp_channel *chan, ++ struct pppoe_opt *addressing) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ struct pppox_sock *po = pppox_sk(sk); ++ int err = 0; ++ ++ *addressing = po->proto.pppoe; ++ if (!addressing->dev) ++ return -ENODEV; ++ ++ dev_hold(addressing->dev); ++ return err; ++} ++ ++/* pppoe_channel_addressing_get() ++ * Return PPPoE channel specific addressing information. ++ */ ++int pppoe_channel_addressing_get(struct ppp_channel *chan, ++ struct pppoe_opt *addressing) ++{ ++ return pppoe_get_addressing(chan, addressing); ++} ++EXPORT_SYMBOL(pppoe_channel_addressing_get); ++ ++static const struct pppoe_channel_ops pppoe_chan_ops = { ++ /* PPPoE specific channel ops */ ++ .get_addressing = pppoe_get_addressing, ++ /* General ppp channel ops */ ++ .ops.start_xmit = pppoe_xmit, ++ .ops.get_channel_protocol = pppoe_get_channel_protocol, ++ .ops.hold = pppoe_hold_chan, ++ .ops.release = pppoe_release_chan, ++ .ops.fill_forward_path = pppoe_fill_forward_path, + }; + + static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -91,4 +91,17 @@ enum { + PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ + }; + ++/* ++ * PPPoE Channel specific operations ++ */ ++struct pppoe_channel_ops { ++ /* Must be first - general to all PPP channels */ ++ struct ppp_channel_ops ops; ++ int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *); ++}; ++ ++/* Return PPPoE channel specific addressing information */ ++extern int pppoe_channel_addressing_get(struct ppp_channel *chan, ++ struct pppoe_opt *addressing); ++ + #endif /* !(__LINUX_IF_PPPOX_H) */ +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -1762,6 +1762,36 @@ enum netdev_priv_flags { + IFF_NO_IP_ALIGN = BIT_ULL(34), + }; + ++/** ++ * enum netdev_priv_flags_ext - &struct net_device priv_flags_ext ++ * ++ * These flags are used to check for device type and can be ++ * set and used by the drivers ++ * ++ * @IFF_EXT_TUN_TAP: device is a TUN/TAP device ++ * @IFF_EXT_PPP_L2TPV2: device is a L2TPV2 device ++ * @IFF_EXT_PPP_L2TPV3: device is a L2TPV3 device ++ * @IFF_EXT_PPP_PPTP: device is a PPTP device ++ * @IFF_EXT_GRE_V4_TAP: device is a GRE IPv4 TAP device ++ * @IFF_EXT_GRE_V6_TAP: device is a GRE IPv6 TAP device ++ * @IFF_EXT_IFB: device is an IFB device ++ * @IFF_EXT_MAPT: device is an MAPT device ++ * @IFF_EXT_HW_NO_OFFLOAD: device is an NON Offload device ++ * @IFF_EXT_L2TPV3: device is a L2TPV3 Ethernet device ++ */ ++enum netdev_priv_flags_ext { ++ IFF_EXT_TUN_TAP = 1<<0, ++ IFF_EXT_PPP_L2TPV2 = 1<<1, ++ IFF_EXT_PPP_L2TPV3 = 1<<2, ++ IFF_EXT_PPP_PPTP = 1<<3, ++ IFF_EXT_GRE_V4_TAP = 1<<4, ++ IFF_EXT_GRE_V6_TAP = 1<<5, ++ IFF_EXT_IFB = 1<<6, ++ IFF_EXT_MAPT = 1<<7, ++ IFF_EXT_HW_NO_OFFLOAD = 1<<8, ++ IFF_EXT_ETH_L2TPV3 = 1<<9, ++}; ++ + #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN + #define IFF_EBRIDGE IFF_EBRIDGE + #define IFF_BONDING IFF_BONDING +@@ -2127,6 +2157,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; +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -19,6 +19,10 @@ + #include + #include + #include ++#include ++ ++#define PPP_CHANNEL_DISCONNECT 0 ++#define PPP_CHANNEL_CONNECT 1 + + struct net_device_path; + struct net_device_path_ctx; +@@ -30,9 +34,19 @@ struct ppp_channel_ops { + int (*start_xmit)(struct ppp_channel *, struct sk_buff *); + /* Handle an ioctl call that has come in via /dev/ppp. */ + int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long); ++ /* Get channel protocol type, one of PX_PROTO_XYZ or specific to ++ * the channel subtype ++ */ ++ int (*get_channel_protocol)(struct ppp_channel *); ++ /* Get channel protocol version */ ++ int (*get_channel_protocol_ver)(struct ppp_channel *); ++ /* Hold the channel from being destroyed */ ++ void (*hold)(struct ppp_channel *); ++ /* Release hold on the channel */ ++ void (*release)(struct ppp_channel *); + int (*fill_forward_path)(struct net_device_path_ctx *, +- struct net_device_path *, +- const struct ppp_channel *); ++ struct net_device_path *, ++ const struct ppp_channel *); + }; + + struct ppp_channel { +@@ -76,6 +90,51 @@ extern int ppp_unit_number(struct ppp_ch + /* Get the device name associated with a channel, or NULL if none */ + extern char *ppp_dev_name(struct ppp_channel *); + ++/* Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ */ ++extern int ppp_channel_get_protocol(struct ppp_channel *); ++ ++/* Call this get protocol version */ ++extern int ppp_channel_get_proto_version(struct ppp_channel *); ++ ++/* Call this to hold a channel */ ++extern bool ppp_channel_hold(struct ppp_channel *); ++ ++/* Call this to release a hold you have upon a channel */ ++extern void ppp_channel_release(struct ppp_channel *); ++ ++/* Release hold on PPP channels */ ++extern void ppp_release_channels(struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Hold PPP channels for the PPP device */ ++extern int ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if ppp xmit lock is locked */ ++extern bool ppp_is_xmit_locked(struct net_device *dev); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int ppp_is_multilink(struct net_device *dev); ++ ++/* Register the PPP channel connect notifier */ ++extern void ppp_channel_connection_register_notify(struct notifier_block *nb); ++ ++/* Unregister the PPP channel connect notifier */ ++extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb); ++ ++/* Update statistics of the PPP net_device by incrementing related ++ * statistics field value with corresponding parameter ++ */ ++extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped); ++ ++ + /* + * SMP locking notes: + * The channel code must ensure that when it calls ppp_unregister_channel, diff --git a/target/linux/qualcommax/patches-6.6/0600-3-qca-nss-ecm-support-net-bonding.patch b/target/linux/qualcommax/patches-6.6/0600-3-qca-nss-ecm-support-net-bonding.patch new file mode 100644 index 00000000000000..4e683e3a3e7e8e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-3-qca-nss-ecm-support-net-bonding.patch @@ -0,0 +1,46 @@ +--- 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; /* QCA NSS ECM bonding support */ + + static const struct flow_dissector_key flow_keys_bonding_keys[] = { + { +@@ -5872,6 +5873,11 @@ static void bond_destructor(struct net_d + if (bond->wq) + destroy_workqueue(bond->wq); + ++ /* QCA NSS ECM bonding support - Start */ ++ if (bond->id != (~0U)) ++ clear_bit(bond->id, &bond_id_mask); ++ /* QCA NSS ECM bonding support - End */ ++ + free_percpu(bond->rr_tx_counter); + } + +@@ -6421,6 +6427,13 @@ int bond_create(struct net *net, const c + + bond_work_init_all(bond); + ++ /* QCA NSS ECM bonding support - Start */ ++ bond->id = ~0U; ++ if (bond_id_mask != (~0UL)) { ++ bond->id = (u32)ffz(bond_id_mask); ++ set_bit(bond->id, &bond_id_mask); ++ } ++ /* QCA NSS ECM bonding support - End */ + out: + rtnl_unlock(); + return res; +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -261,6 +261,7 @@ struct bonding { + spinlock_t ipsec_lock; + #endif /* CONFIG_XFRM_OFFLOAD */ + struct bpf_prog *xdp_prog; ++ u32 id;/* QCA NSS ECM bonding support */ + }; + + #define bond_slave_get_rcu(dev) \ diff --git a/target/linux/qualcommax/patches-6.6/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch b/target/linux/qualcommax/patches-6.6/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch new file mode 100644 index 00000000000000..e02e9090db8c4e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-4-qca-nss-ecm-support-net-bonding-over-LAG-interface.patch @@ -0,0 +1,685 @@ +--- a/drivers/net/bonding/bond_3ad.c ++++ b/drivers/net/bonding/bond_3ad.c +@@ -116,6 +116,40 @@ static void ad_marker_response_received( + struct port *port); + static void ad_update_actor_keys(struct port *port, bool reset); + ++/* QCA NSS ECM bonding support - Start */ ++struct bond_cb __rcu *bond_cb; ++ ++int bond_register_cb(struct bond_cb *cb) ++{ ++ struct bond_cb *lag_cb; ++ ++ lag_cb = kzalloc(sizeof(*lag_cb), GFP_ATOMIC | __GFP_NOWARN); ++ if (!lag_cb) { ++ return -1; ++ } ++ ++ memcpy((void *)lag_cb, (void *)cb, sizeof(*cb)); ++ ++ rcu_read_lock(); ++ rcu_assign_pointer(bond_cb, lag_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(bond_register_cb); ++ ++void bond_unregister_cb(void) ++{ ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ rcu_assign_pointer(bond_cb, NULL); ++ rcu_read_unlock(); ++ ++ kfree(lag_cb_main); ++} ++EXPORT_SYMBOL(bond_unregister_cb); ++/* QCA NSS ECM bonding support - End */ + + /* ================= api to bonding and kernel code ================== */ + +@@ -1073,7 +1107,31 @@ static void ad_mux_machine(struct port * + ad_disable_collecting_distributing(port, + update_slave_arr); + port->ntt = true; ++ ++ /* QCA NSS ECM bonding support - Start */ ++ /* Send a notificaton about change in state of this ++ * port. We only want to handle case where port moves ++ * from AD_MUX_COLLECTING_DISTRIBUTING -> ++ * AD_MUX_ATTACHED. ++ */ ++ if (bond_slave_is_up(port->slave) && ++ (last_state == AD_MUX_COLLECTING_DISTRIBUTING)) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_down) { ++ struct net_device *dev; ++ ++ dev = port->slave->dev; ++ lag_cb_main->bond_cb_link_down(dev); ++ } ++ rcu_read_unlock(); ++ } ++ + break; ++ /* QCA NSS ECM bonding support - End */ + case AD_MUX_COLLECTING_DISTRIBUTING: + port->actor_oper_port_state |= LACP_STATE_COLLECTING; + port->actor_oper_port_state |= LACP_STATE_DISTRIBUTING; +@@ -1917,6 +1975,7 @@ static void ad_enable_collecting_distrib + bool *update_slave_arr) + { + if (port->aggregator->is_active) { ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + slave_dbg(port->slave->bond->dev, port->slave->dev, + "Enabling port %d (LAG %d)\n", + port->actor_port_number, +@@ -1924,6 +1983,16 @@ static void ad_enable_collecting_distrib + __enable_port(port); + /* Slave array needs update */ + *update_slave_arr = true; ++ ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(port->slave->dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ + } + } + +@@ -2683,6 +2752,104 @@ int bond_3ad_get_active_agg_info(struct + return ret; + } + ++/* QCA NSS ECM bonding support - Start */ ++/* bond_3ad_get_tx_dev - Calculate egress interface for a given packet, ++ * for a LAG that is configured in 802.3AD mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address ++ * @dst: pointer to destination L3 address ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash is used to calculate hash using L2/L3 ++ * addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, u8 *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct aggregator *agg; ++ struct ad_info ad_info; ++ struct list_head *iter; ++ struct slave *slave; ++ struct slave *first_ok_slave = NULL; ++ u32 hash = 0; ++ int slaves_in_agg; ++ int slave_agg_no = 0; ++ int agg_id; ++ ++ if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { ++ pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ slaves_in_agg = ad_info.ports; ++ agg_id = ad_info.aggregator_id; ++ ++ if (slaves_in_agg == 0) { ++ pr_debug("%s: Error: active aggregator is empty\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_agg_no = hash % slaves_in_agg; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for 802.3AD fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ slave_agg_no = hash % slaves_in_agg; ++ } ++ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ agg = SLAVE_AD_INFO(slave)->port.aggregator; ++ if (!agg || agg->aggregator_identifier != agg_id) ++ continue; ++ ++ if (slave_agg_no >= 0) { ++ if (!first_ok_slave && bond_slave_can_tx(slave)) ++ first_ok_slave = slave; ++ slave_agg_no--; ++ continue; ++ } ++ ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ if (slave_agg_no >= 0) { ++ pr_err("%s: Error: Couldn't find a slave to tx on for aggregator ID %d\n", ++ bond_dev->name, agg_id); ++ return NULL; ++ } ++ ++ /* we couldn't find any suitable slave after the agg_no, so use the ++ * first suitable found, if found. ++ */ ++ if (first_ok_slave) ++ return first_ok_slave->dev; ++ ++ return NULL; ++} ++/* QCA NSS ECM bonding support - End */ ++ + int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) + { +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -288,6 +288,21 @@ const char *bond_mode_name(int mode) + return names[mode]; + } + ++/* QCA NSS ECM bonding support */ ++int bond_get_id(struct net_device *bond_dev) ++{ ++ struct bonding *bond; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return -EINVAL; ++ ++ bond = netdev_priv(bond_dev); ++ return bond->id; ++} ++EXPORT_SYMBOL(bond_get_id); ++/* QCA NSS ECM bonding support */ ++ + /** + * bond_dev_queue_xmit - Prepare skb for xmit. + * +@@ -1189,6 +1204,23 @@ void bond_change_active_slave(struct bon + if (BOND_MODE(bond) == BOND_MODE_8023AD) + bond_3ad_handle_link_change(new_active, BOND_LINK_UP); + ++ /* QCA NSS ECM bonding support - Start */ ++ if (bond->params.mode == BOND_MODE_XOR) { ++ struct bond_cb *lag_cb_main; ++ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && ++ lag_cb_main->bond_cb_link_up) { ++ struct net_device *dev; ++ ++ dev = new_active->dev; ++ lag_cb_main->bond_cb_link_up(dev); ++ } ++ rcu_read_unlock(); ++ } ++ /* QCA NSS ECM bonding support - End */ ++ + if (bond_is_lb(bond)) + bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); + } else { +@@ -1833,6 +1865,7 @@ int bond_enslave(struct net_device *bond + const struct net_device_ops *slave_ops = slave_dev->netdev_ops; + struct slave *new_slave = NULL, *prev_slave; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + int link_reporting; + int res = 0, i; + +@@ -2278,6 +2311,15 @@ int bond_enslave(struct net_device *bond + bond_is_active_slave(new_slave) ? "an active" : "a backup", + new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + /* enslave is successful */ + bond_queue_slave_event(new_slave); + return 0; +@@ -2343,6 +2385,15 @@ err_undo_flags: + } + } + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_enslave) ++ lag_cb_main->bond_cb_enslave(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + return res; + } + +@@ -2364,6 +2415,7 @@ static int __bond_release_one(struct net + struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave, *oldcurrent; + struct sockaddr_storage ss; ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + int old_flags = bond_dev->flags; + netdev_features_t old_features = bond_dev->features; + +@@ -2386,6 +2438,15 @@ static int __bond_release_one(struct net + + bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ if (lag_cb_main && lag_cb_main->bond_cb_release) ++ lag_cb_main->bond_cb_release(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + bond_sysfs_slave_del(slave); + + /* recompute stats just before removing the slave */ +@@ -2708,6 +2769,8 @@ static void bond_miimon_commit(struct bo + struct slave *slave, *primary, *active; + bool do_failover = false; + struct list_head *iter; ++ struct net_device *slave_dev = NULL; /* QCA NSS ECM bonding support */ ++ struct bond_cb *lag_cb_main; /* QCA NSS ECM bonding support */ + + ASSERT_RTNL(); + +@@ -2747,6 +2810,12 @@ static void bond_miimon_commit(struct bo + bond_set_active_slave(slave); + } + ++ /* QCA NSS ECM bonding support - Start */ ++ if ((bond->params.mode == BOND_MODE_XOR) && ++ (!slave_dev)) ++ slave_dev = slave->dev; ++ /* QCA NSS ECM bonding support - End */ ++ + slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", + slave->speed == SPEED_UNKNOWN ? 0 : slave->speed, + slave->duplex ? "full" : "half"); +@@ -2795,6 +2864,16 @@ static void bond_miimon_commit(struct bo + unblock_netpoll_tx(); + } + ++ /* QCA NSS ECM bonding support - Start */ ++ rcu_read_lock(); ++ lag_cb_main = rcu_dereference(bond_cb); ++ ++ if (slave_dev && lag_cb_main && lag_cb_main->bond_cb_link_up) ++ lag_cb_main->bond_cb_link_up(slave_dev); ++ ++ rcu_read_unlock(); ++ /* QCA NSS ECM bonding support - End */ ++ + bond_set_carrier(bond); + } + +@@ -4047,8 +4126,219 @@ static inline u32 bond_eth_hash(struct s + return 0; + + ep = (struct ethhdr *)(data + mhoff); +- return ep->h_dest[5] ^ ep->h_source[5] ^ be16_to_cpu(ep->h_proto); ++ return ep->h_dest[5] ^ ep->h_source[5]; /* QCA NSS ECM bonding support */ ++} ++ ++/* QCA NSS ECM bonding support - Start */ ++/* Extract the appropriate headers based on bond's xmit policy */ ++static bool bond_flow_dissect_without_skb(struct bonding *bond, ++ u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, ++ u16 protocol, __be16 *layer4hdr, ++ struct flow_keys *fk) ++{ ++ u32 *src = NULL; ++ u32 *dst = NULL; ++ ++ fk->ports.ports = 0; ++ src = (uint32_t *)psrc; ++ dst = (uint32_t *)pdst; ++ ++ if (protocol == htons(ETH_P_IP)) { ++ /* V4 addresses and address type*/ ++ fk->addrs.v4addrs.src = src[0]; ++ fk->addrs.v4addrs.dst = dst[0]; ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; ++ } else if (protocol == htons(ETH_P_IPV6)) { ++ /* V6 addresses and address type*/ ++ memcpy(&fk->addrs.v6addrs.src, src, sizeof(struct in6_addr)); ++ memcpy(&fk->addrs.v6addrs.dst, dst, sizeof(struct in6_addr)); ++ fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; ++ } else { ++ return false; ++ } ++ if ((bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) && ++ (layer4hdr)) ++ fk->ports.ports = *layer4hdr; ++ ++ return true; ++} ++ ++/* bond_xmit_hash_without_skb - Applies load balancing algorithm for a packet, ++ * to calculate hash for a given set of L2/L3 addresses. Does not ++ * calculate egress interface. ++ */ ++uint32_t bond_xmit_hash_without_skb(u8 *src_mac, u8 *dst_mac, ++ void *psrc, void *pdst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ struct flow_keys flow; ++ u32 hash = 0; ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || ++ !bond_flow_dissect_without_skb(bond, src_mac, dst_mac, psrc, ++ pdst, protocol, layer4hdr, &flow)) ++ return (dst_mac[5] ^ src_mac[5]); ++ ++ if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23) ++ hash = dst_mac[5] ^ src_mac[5]; ++ else if (layer4hdr) ++ hash = (__force u32)flow.ports.ports; ++ ++ hash ^= (__force u32)flow_get_u32_dst(&flow) ^ ++ (__force u32)flow_get_u32_src(&flow); ++ hash ^= (hash >> 16); ++ hash ^= (hash >> 8); ++ ++ return hash; ++} ++ ++/* bond_xor_get_tx_dev - Calculate egress interface for a given packet for a LAG ++ * that is configured in balance-xor mode ++ * @skb: pointer to skb to be egressed ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol ++ * @bond_dev: pointer to bond master device ++ * ++ * If @skb is NULL, bond_xmit_hash_without_skb is used to calculate hash using ++ * L2/L3 addresses. ++ * ++ * Returns: Either valid slave device, or NULL otherwise ++ */ ++static struct net_device *bond_xor_get_tx_dev(struct sk_buff *skb, ++ u8 *src_mac, u8 *dst_mac, ++ void *src, void *dst, ++ u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond = netdev_priv(bond_dev); ++ int slave_cnt = READ_ONCE(bond->slave_cnt); ++ int slave_id = 0, i = 0; ++ u32 hash; ++ struct list_head *iter; ++ struct slave *slave; ++ ++ if (slave_cnt == 0) { ++ pr_debug("%s: Error: No slave is attached to the interface\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ if (skb) { ++ hash = bond_xmit_hash(bond, skb); ++ slave_id = hash % slave_cnt; ++ } else { ++ if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && ++ bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { ++ pr_debug("%s: Error: Unsupported hash policy for balance-XOR fast path\n", ++ bond_dev->name); ++ return NULL; ++ } ++ ++ hash = bond_xmit_hash_without_skb(src_mac, dst_mac, src, ++ dst, protocol, bond_dev, ++ layer4hdr); ++ slave_id = hash % slave_cnt; ++ } ++ ++ i = slave_id; ++ ++ /* Here we start from the slave with slave_id */ ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) { ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ } ++ ++ /* Here we start from the first slave up to slave_id */ ++ i = slave_id; ++ bond_for_each_slave_rcu(bond, slave, iter) { ++ if (--i < 0) ++ break; ++ if (bond_slave_can_tx(slave)) ++ return slave->dev; ++ } ++ ++ return NULL; ++} ++ ++/* bond_get_tx_dev - Calculate egress interface for a given packet. ++ * ++ * Supports 802.3AD and balance-xor modes ++ * ++ * @skb: pointer to skb to be egressed, if valid ++ * @src_mac: pointer to source L2 address ++ * @dst_mac: pointer to destination L2 address ++ * @src: pointer to source L3 address in network order ++ * @dst: pointer to destination L3 address in network order ++ * @protocol: L3 protocol id from L2 header ++ * @bond_dev: pointer to bond master device ++ * ++ * Returns: Either valid slave device, or NULL for un-supported LAG modes ++ */ ++struct net_device *bond_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ u8 *dst_mac, void *src, ++ void *dst, u16 protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr) ++{ ++ struct bonding *bond; ++ ++ if (!bond_dev) ++ return NULL; ++ ++ if (!((bond_dev->priv_flags & IFF_BONDING) && ++ (bond_dev->flags & IFF_MASTER))) ++ return NULL; ++ ++ bond = netdev_priv(bond_dev); ++ ++ switch (bond->params.mode) { ++ case BOND_MODE_XOR: ++ return bond_xor_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ case BOND_MODE_8023AD: ++ return bond_3ad_get_tx_dev(skb, src_mac, dst_mac, ++ src, dst, protocol, ++ bond_dev, layer4hdr); ++ default: ++ return NULL; ++ } + } ++EXPORT_SYMBOL(bond_get_tx_dev); ++ ++/* In bond_xmit_xor() , we determine the output device by using a pre- ++ * determined xmit_hash_policy(), If the selected device is not enabled, ++ * find the next active slave. ++ */ ++static int bond_xmit_xor(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct bonding *bond = netdev_priv(dev); ++ struct net_device *outdev; ++ ++ outdev = bond_xor_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) ++ goto out; ++ ++ bond_dev_queue_xmit(bond, skb, outdev); ++ goto final; ++out: ++ /* no suitable interface, frame not sent */ ++ dev_kfree_skb(skb); ++final: ++ return NETDEV_TX_OK; ++} ++/* QCA NSS ECM bonding support - End */ + + static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk, const void *data, + int hlen, __be16 l2_proto, int *nhoff, int *ip_proto, bool l34) +@@ -5177,15 +5467,18 @@ static netdev_tx_t bond_3ad_xor_xmit(str + struct net_device *dev) + { + struct bonding *bond = netdev_priv(dev); +- struct bond_up_slave *slaves; +- struct slave *slave; ++ /* QCA NSS ECM bonding support - Start */ ++ struct net_device *outdev = NULL; + +- slaves = rcu_dereference(bond->usable_slaves); +- slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves); +- if (likely(slave)) +- return bond_dev_queue_xmit(bond, skb, slave->dev); ++ outdev = bond_3ad_get_tx_dev(skb, NULL, NULL, NULL, ++ NULL, 0, dev, NULL); ++ if (!outdev) { ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++ } + +- return bond_tx_drop(dev, skb); ++ return bond_dev_queue_xmit(bond, skb, outdev); ++ /* QCA NSS ECM bonding support - End */ + } + + /* in broadcast mode, we send everything to all usable interfaces. */ +@@ -5435,8 +5728,9 @@ static netdev_tx_t __bond_start_xmit(str + return bond_xmit_roundrobin(skb, dev); + case BOND_MODE_ACTIVEBACKUP: + return bond_xmit_activebackup(skb, dev); +- case BOND_MODE_8023AD: + case BOND_MODE_XOR: ++ return bond_xmit_xor(skb, dev); /* QCA NSS ECM bonding support */ ++ case BOND_MODE_8023AD: + return bond_3ad_xor_xmit(skb, dev); + case BOND_MODE_BROADCAST: + return bond_xmit_broadcast(skb, dev); +--- a/include/net/bond_3ad.h ++++ b/include/net/bond_3ad.h +@@ -302,8 +302,15 @@ 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); ++/* QCA NSS ECM bonding support */ ++struct net_device *bond_3ad_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, ++ uint8_t *dst_mac, void *src, ++ void *dst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++/* QCA NSS ECM bonding support */ ++ + void bond_3ad_update_ad_actor_settings(struct bonding *bond); + int bond_3ad_stats_fill(struct sk_buff *skb, struct bond_3ad_stats *stats); + size_t bond_3ad_stats_size(void); + #endif /* _NET_BOND_3AD_H */ +- +--- a/include/net/bonding.h ++++ b/include/net/bonding.h +@@ -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; /* QCA NSS ECM bonding support */ ++ + #ifdef CONFIG_NET_POLL_CONTROLLER + extern atomic_t netpoll_block_tx; + +@@ -653,6 +655,7 @@ struct bond_net { + + int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); + netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); ++int bond_get_id(struct net_device *bond_dev); /* QCA NSS ECM bonding support */ + int bond_create(struct net *net, const char *name); + int bond_create_sysfs(struct bond_net *net); + void bond_destroy_sysfs(struct bond_net *net); +@@ -684,6 +687,13 @@ struct bond_vlan_tag *bond_verify_device + int level); + int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); + void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); ++/* QCA NSS ECM bonding support - Start */ ++uint32_t bond_xmit_hash_without_skb(uint8_t *src_mac, uint8_t *dst_mac, ++ void *psrc, void *pdst, uint16_t protocol, ++ struct net_device *bond_dev, ++ __be16 *layer4hdr); ++/* QCA NSS ECM bonding support - End */ ++ + void bond_work_init_all(struct bonding *bond); + + #ifdef CONFIG_PROC_FS +@@ -788,4 +798,18 @@ static inline netdev_tx_t bond_tx_drop(s + return NET_XMIT_DROP; + } + ++/* QCA NSS ECM bonding support - Start */ ++struct bond_cb { ++ void (*bond_cb_link_up)(struct net_device *slave); ++ void (*bond_cb_link_down)(struct net_device *slave); ++ void (*bond_cb_enslave)(struct net_device *slave); ++ void (*bond_cb_release)(struct net_device *slave); ++ void (*bond_cb_delete_by_slave)(struct net_device *slave); ++ void (*bond_cb_delete_by_mac)(uint8_t *mac_addr); ++}; ++ ++extern int bond_register_cb(struct bond_cb *cb); ++extern void bond_unregister_cb(void); ++/* QCA NSS ECM bonding support - End */ ++ + #endif /* _NET_BONDING_H */ diff --git a/target/linux/qualcommax/patches-6.6/0600-5-qca-nss-ecm-support-macvlan.patch b/target/linux/qualcommax/patches-6.6/0600-5-qca-nss-ecm-support-macvlan.patch new file mode 100644 index 00000000000000..29f7e96d791328 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-5-qca-nss-ecm-support-macvlan.patch @@ -0,0 +1,96 @@ +--- a/include/linux/if_macvlan.h ++++ b/include/linux/if_macvlan.h +@@ -15,6 +15,13 @@ struct macvlan_port; + #define MACVLAN_MC_FILTER_BITS 8 + #define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS) + ++/* QCA NSS ECM Support - Start */ ++/* ++ * Callback for updating interface statistics for macvlan flows offloaded from host CPU. ++ */ ++typedef void (*macvlan_offload_stats_update_cb_t)(struct net_device *dev, struct rtnl_link_stats64 *stats, bool update_mcast_rx_stats); ++/* QCA NSS ECM Support - End */ ++ + struct macvlan_dev { + struct net_device *dev; + struct list_head list; +@@ -35,6 +42,7 @@ struct macvlan_dev { + #ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; + #endif ++ macvlan_offload_stats_update_cb_t offload_stats_update; /* QCA NSS ECM support */ + }; + + static inline void macvlan_count_rx(const struct macvlan_dev *vlan, +@@ -107,4 +115,26 @@ static inline int macvlan_release_l2fw_o + macvlan->accel_priv = NULL; + return dev_uc_add(macvlan->lowerdev, dev->dev_addr); + } ++ ++/* QCA NSS ECM Support - Start */ ++#if IS_ENABLED(CONFIG_MACVLAN) ++static inline void ++macvlan_offload_stats_update(struct net_device *dev, ++ struct rtnl_link_stats64 *stats, ++ bool update_mcast_rx_stats) ++{ ++ struct macvlan_dev *macvlan = netdev_priv(dev); ++ ++ macvlan->offload_stats_update(dev, stats, update_mcast_rx_stats); ++} ++ ++static inline enum ++macvlan_mode macvlan_get_mode(struct net_device *dev) ++{ ++ struct macvlan_dev *macvlan = netdev_priv(dev); ++ ++ return macvlan->mode; ++} ++#endif ++/* QCA NSS ECM Support - End */ + #endif /* _LINUX_IF_MACVLAN_H */ +--- a/drivers/net/macvlan.c ++++ b/drivers/net/macvlan.c +@@ -960,6 +960,34 @@ static void macvlan_uninit(struct net_de + macvlan_port_destroy(port->dev); + } + ++/* QCA NSS ECM Support - Start */ ++/* Update macvlan statistics processed by offload engines */ ++static void macvlan_dev_update_stats(struct net_device *dev, ++ struct rtnl_link_stats64 *offl_stats, ++ bool update_mcast_rx_stats) ++{ ++ struct vlan_pcpu_stats *stats; ++ struct macvlan_dev *macvlan; ++ ++ /* Is this a macvlan? */ ++ if (!netif_is_macvlan(dev)) ++ return; ++ ++ macvlan = netdev_priv(dev); ++ stats = this_cpu_ptr(macvlan->pcpu_stats); ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_packets, offl_stats->rx_packets); ++ u64_stats_add(&stats->rx_bytes, offl_stats->rx_bytes); ++ u64_stats_add(&stats->tx_packets, offl_stats->tx_packets); ++ u64_stats_add(&stats->tx_bytes, offl_stats->tx_bytes); ++ /* Update multicast statistics */ ++ if (unlikely(update_mcast_rx_stats)) { ++ u64_stats_add(&stats->rx_multicast, offl_stats->rx_packets); ++ } ++ u64_stats_update_end(&stats->syncp); ++} ++/* QCA NSS ECM Support - End */ ++ + static void macvlan_dev_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -1506,6 +1534,7 @@ int macvlan_common_newlink(struct net *s + vlan->dev = dev; + vlan->port = port; + vlan->set_features = MACVLAN_FEATURES; ++ vlan->offload_stats_update = macvlan_dev_update_stats; /* QCA NSS ECM Support */ + + vlan->mode = MACVLAN_MODE_VEPA; + if (data && data[IFLA_MACVLAN_MODE]) diff --git a/target/linux/qualcommax/patches-6.6/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch b/target/linux/qualcommax/patches-6.6/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch new file mode 100644 index 00000000000000..27650731a69612 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-6-qca-nss-ecm-support-netfilter-DSCPREMARK.patch @@ -0,0 +1,154 @@ +--- 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/include/net/netfilter/nf_conntrack_extend.h ++++ b/include/net/netfilter/nf_conntrack_extend.h +@@ -31,6 +31,10 @@ enum nf_ct_ext_id { + #if IS_ENABLED(CONFIG_NET_ACT_CT) + NF_CT_EXT_ACT_CT, + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ NF_CT_EXT_DSCPREMARK, /* QCA NSS ECM support */ ++#endif ++ + NF_CT_EXT_NUM, + }; + +--- a/net/netfilter/nf_conntrack_extend.c ++++ b/net/netfilter/nf_conntrack_extend.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + + #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ +@@ -54,6 +55,9 @@ static const u8 nf_ct_ext_type_len[NF_CT + #if IS_ENABLED(CONFIG_NET_ACT_CT) + [NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ [NF_CT_EXT_DSCPREMARK] = sizeof(struct nf_ct_dscpremark_ext), ++#endif + }; + + static __always_inline unsigned int total_extension_size(void) +@@ -86,6 +90,9 @@ static __always_inline unsigned int tota + #if IS_ENABLED(CONFIG_NET_ACT_CT) + + sizeof(struct nf_conn_act_ct_ext) + #endif ++#ifdef CONFIG_NF_CONNTRACK_DSCPREMARK_EXT ++ + sizeof(struct nf_ct_dscpremark_ext) ++#endif + ; + } + +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -15,6 +15,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_OVS) + nf_conntrack-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o + nf_conntrack-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o + nf_conntrack-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o ++nf_conntrack-$(CONFIG_NF_CONNTRACK_DSCPREMARK_EXT) += nf_conntrack_dscpremark_ext.o + ifeq ($(CONFIG_NF_CONNTRACK),m) + nf_conntrack-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_conntrack_bpf.o + else ifeq ($(CONFIG_NF_CONNTRACK),y) +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -45,6 +45,9 @@ + #include + #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/qualcommax/patches-6.6/0600-7-qca-nss-ecm-fix-IPv6-user-route-change-event-calls.patch b/target/linux/qualcommax/patches-6.6/0600-7-qca-nss-ecm-fix-IPv6-user-route-change-event-calls.patch new file mode 100644 index 00000000000000..e3081a79aafdbe --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0600-7-qca-nss-ecm-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 +@@ -3867,10 +3867,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; +@@ -3892,9 +3888,6 @@ static int __ip6_del_rt(struct fib6_info + err = fib6_del(rt, info); + spin_unlock_bh(&table->tb6_lock); + +- if (!err) +- atomic_notifier_call_chain(&ip6route_chain, +- RTM_DELROUTE, rt); + out: + fib6_info_release(rt); + return err; +@@ -4500,6 +4493,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; + } + +@@ -5518,11 +5515,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, +@@ -5539,9 +5542,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/qualcommax/patches-6.6/0601-1-qca-add-nss-bridge-mgr-support.patch b/target/linux/qualcommax/patches-6.6/0601-1-qca-add-nss-bridge-mgr-support.patch new file mode 100644 index 00000000000000..dcfd78d5932a6a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0601-1-qca-add-nss-bridge-mgr-support.patch @@ -0,0 +1,92 @@ +From 3c17a0e1112be70071e98d5208da5b55dcec20a6 Mon Sep 17 00:00:00 2001 +From: Simon Casey +Date: Wed, 2 Feb 2022 19:37:29 +0100 +Subject: [PATCH] Update 607-qca-add-add-nss-bridge-mgr-support.patch for kernel 5.15 + +--- + include/linux/if_bridge.h | 4 ++++ + net/bridge/br_fdb.c | 25 +++++++++++++++++++++---- + 2 files changed, 25 insertions(+), 4 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -254,4 +254,8 @@ typedef struct net_bridge_port *br_get_d + extern br_get_dst_hook_t __rcu *br_get_dst_hook; + /* QCA NSS ECM support - End */ + ++/* QCA NSS bridge-mgr support - Start */ ++extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); ++/* QCA NSS bridge-mgr support - End */ ++ + #endif +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -576,7 +576,7 @@ void br_fdb_cleanup(struct work_struct * + unsigned long delay = hold_time(br); + unsigned long work_delay = delay; + unsigned long now = jiffies; +- u8 mac_addr[6]; /* QCA NSS ECM support */ ++ struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */ + + /* this part is tricky, in order to avoid blocking learning and + * consequently forwarding, we rely on rcu to delete objects with +@@ -604,12 +604,13 @@ void br_fdb_cleanup(struct work_struct * + } else { + spin_lock_bh(&br->hash_lock); + if (!hlist_unhashed(&f->fdb_node)) { +- ether_addr_copy(mac_addr, f->key.addr.addr); ++ memset(&fdb_event, 0, sizeof(fdb_event)); ++ ether_addr_copy(fdb_event.addr, f->key.addr.addr); + fdb_delete(br, f, true); + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, 0, +- (void *)mac_addr); ++ (void *)&fdb_event); + /* QCA NSS ECM support - End */ + } + spin_unlock_bh(&br->hash_lock); +@@ -907,10 +908,21 @@ static bool __fdb_mark_active(struct net + test_and_clear_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags)); + } + ++/* QCA NSS bridge-mgr support - Start */ ++/* Get the bridge device */ ++struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br) ++{ ++ dev_hold(br->dev); ++ return br->dev; ++} ++EXPORT_SYMBOL_GPL(br_fdb_bridge_dev_get_and_hold); ++/* QCA NSS bridge-mgr support - End */ ++ + void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid, unsigned long flags) + { + struct net_bridge_fdb_entry *fdb; ++ struct br_fdb_event fdb_event; /* QCA NSS bridge-mgr support */ + + /* some users want to always flood. */ + if (hold_time(br) == 0) +@@ -936,6 +948,12 @@ void br_fdb_update(struct net_bridge *br + if (unlikely(source != READ_ONCE(fdb->dst) && + !test_bit(BR_FDB_STICKY, &fdb->flags))) { + br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH); ++ /* QCA NSS bridge-mgr support - Start */ ++ ether_addr_copy(fdb_event.addr, addr); ++ fdb_event.br = br; ++ fdb_event.orig_dev = fdb->dst->dev; ++ fdb_event.dev = source->dev; ++ /* QCA NSS bridge-mgr support - End */ + WRITE_ONCE(fdb->dst, source); + fdb_modified = true; + /* Take over HW learned entry */ +@@ -952,7 +970,7 @@ void br_fdb_update(struct net_bridge *br + /* QCA NSS ECM support - Start */ + atomic_notifier_call_chain( + &br_fdb_update_notifier_list, +- 0, (void *)addr); ++ 0, (void *)&fdb_event); + /* QCA NSS ECM support - End */ + } + diff --git a/target/linux/qualcommax/patches-6.6/0602-1-qca-nss-drv-add-qdisc-support.patch b/target/linux/qualcommax/patches-6.6/0602-1-qca-nss-drv-add-qdisc-support.patch new file mode 100644 index 00000000000000..2926faacefebd7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0602-1-qca-nss-drv-add-qdisc-support.patch @@ -0,0 +1,25 @@ +--- a/include/uapi/linux/pkt_cls.h ++++ b/include/uapi/linux/pkt_cls.h +@@ -139,6 +139,7 @@ enum tca_id { + TCA_ID_MPLS, + TCA_ID_CT, + TCA_ID_GATE, ++ TCA_ID_MIRRED_NSS, /* QCA NSS Qdisc IGS Support */ + /* other actions go here */ + __TCA_ID_MAX = 255 + }; +@@ -817,4 +818,14 @@ enum { + TCF_EM_OPND_LT + }; + ++/* QCA NSS Qdisc Support - Start */ ++#define _TC_MAKE32(x) ((x)) ++#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) ++ ++#define TC_NCLS _TC_MAKEMASK1(8) ++#define TC_NCLS_NSS _TC_MAKEMASK1(12) ++#define SET_TC_NCLS_NSS(v) ( TC_NCLS_NSS | ((v) & ~TC_NCLS_NSS)) ++#define CLR_TC_NCLS_NSS(v) ( (v) & ~TC_NCLS_NSS) ++/* QCA NSS Qdisc Support - End */ ++ + #endif diff --git a/target/linux/qualcommax/patches-6.6/0603-1-qca-nss-clients-add-qdisc-support.patch b/target/linux/qualcommax/patches-6.6/0603-1-qca-nss-clients-add-qdisc-support.patch new file mode 100644 index 00000000000000..9220aca1c78be7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-1-qca-nss-clients-add-qdisc-support.patch @@ -0,0 +1,463 @@ +--- a/include/linux/timer.h ++++ b/include/linux/timer.h +@@ -17,6 +17,7 @@ struct timer_list { + unsigned long expires; + void (*function)(struct timer_list *); + u32 flags; ++ unsigned long cust_data; + + #ifdef CONFIG_LOCKDEP + struct lockdep_map lockdep_map; +--- a/drivers/net/ifb.c ++++ b/drivers/net/ifb.c +@@ -151,6 +151,31 @@ resched: + + } + ++void ifb_update_offload_stats(struct net_device *dev, struct pcpu_sw_netstats *offload_stats) ++{ ++ struct ifb_dev_private *dp; ++ struct ifb_q_private *txp; ++ ++ if (!dev || !offload_stats) { ++ return; ++ } ++ ++ if (!(dev->priv_flags_ext & IFF_EXT_IFB)) { ++ return; ++ } ++ ++ dp = netdev_priv(dev); ++ txp = dp->tx_private; ++ ++ u64_stats_update_begin(&txp->rx_stats.sync); ++ txp->rx_stats.packets += u64_stats_read(&offload_stats->rx_packets); ++ txp->rx_stats.bytes += u64_stats_read(&offload_stats->rx_bytes); ++ txp->tx_stats.packets += u64_stats_read(&offload_stats->tx_packets); ++ txp->tx_stats.bytes += u64_stats_read(&offload_stats->tx_bytes); ++ u64_stats_update_end(&txp->rx_stats.sync); ++} ++EXPORT_SYMBOL(ifb_update_offload_stats); ++ + static void ifb_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) + { +@@ -326,6 +351,7 @@ static void ifb_setup(struct net_device + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; ++ dev->priv_flags_ext |= IFF_EXT_IFB; /* Mark the device as an IFB device. */ + netif_keep_dst(dev); + eth_hw_addr_random(dev); + dev->needs_free_netdev = true; +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -4696,6 +4696,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 +@@ -5222,6 +5231,11 @@ static inline bool netif_is_failover_sla + return dev->priv_flags & IFF_FAILOVER_SLAVE; + } + ++static inline bool netif_is_ifb_dev(const struct net_device *dev) ++{ ++ return dev->priv_flags_ext & IFF_EXT_IFB; ++} ++ + /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */ + static inline void netif_keep_dst(struct net_device *dev) + { +--- a/include/uapi/linux/pkt_sched.h ++++ b/include/uapi/linux/pkt_sched.h +@@ -1306,4 +1306,248 @@ enum { + + #define TCA_ETS_MAX (__TCA_ETS_MAX - 1) + ++/* QCA NSS Clients Support - Start */ ++enum { ++ TCA_NSS_ACCEL_MODE_NSS_FW, ++ TCA_NSS_ACCEL_MODE_PPE, ++ TCA_NSS_ACCEL_MODE_MAX ++}; ++ ++/* NSSFIFO section */ ++ ++enum { ++ TCA_NSSFIFO_UNSPEC, ++ TCA_NSSFIFO_PARMS, ++ __TCA_NSSFIFO_MAX ++}; ++ ++#define TCA_NSSFIFO_MAX (__TCA_NSSFIFO_MAX - 1) ++ ++struct tc_nssfifo_qopt { ++ __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRED section */ ++ ++enum { ++ TCA_NSSWRED_UNSPEC, ++ TCA_NSSWRED_PARMS, ++ __TCA_NSSWRED_MAX ++}; ++ ++#define TCA_NSSWRED_MAX (__TCA_NSSWRED_MAX - 1) ++#define NSSWRED_CLASS_MAX 6 ++struct tc_red_alg_parameter { ++ __u32 min; /* qlen_avg < min: pkts are all enqueued */ ++ __u32 max; /* qlen_avg > max: pkts are all dropped */ ++ __u32 probability;/* Drop probability at qlen_avg = max */ ++ __u32 exp_weight_factor;/* exp_weight_factor for calculate qlen_avg */ ++}; ++ ++struct tc_nsswred_traffic_class { ++ __u32 limit; /* Queue length */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* Parameters for RED alg */ ++}; ++ ++/* ++ * Weight modes for WRED ++ */ ++enum tc_nsswred_weight_modes { ++ TC_NSSWRED_WEIGHT_MODE_DSCP = 0,/* Weight mode is DSCP */ ++ TC_NSSWRED_WEIGHT_MODES, /* Must be last */ ++}; ++ ++struct tc_nsswred_qopt { ++ __u32 limit; /* Queue length */ ++ enum tc_nsswred_weight_modes weight_mode; ++ /* Weight mode */ ++ __u32 traffic_classes; /* How many traffic classes: DPs */ ++ __u32 def_traffic_class; /* Default traffic if no match: def_DP */ ++ __u32 traffic_id; /* The traffic id to be configured: DP */ ++ __u32 weight_mode_value; /* Weight mode value */ ++ struct tc_red_alg_parameter rap;/* RED algorithm parameters */ ++ struct tc_nsswred_traffic_class tntc[NSSWRED_CLASS_MAX]; ++ /* Traffic settings for dumpping */ ++ __u8 ecn; /* Setting ECN bit or dropping */ ++ __u8 set_default; /* Sets qdisc to be the default for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSCODEL section */ ++ ++enum { ++ TCA_NSSCODEL_UNSPEC, ++ TCA_NSSCODEL_PARMS, ++ __TCA_NSSCODEL_MAX ++}; ++ ++#define TCA_NSSCODEL_MAX (__TCA_NSSCODEL_MAX - 1) ++ ++struct tc_nsscodel_qopt { ++ __u32 target; /* Acceptable queueing delay */ ++ __u32 limit; /* Max number of packets that can be held in the queue */ ++ __u32 interval; /* Monitoring interval */ ++ __u32 flows; /* Number of flow buckets */ ++ __u32 quantum; /* Weight (in bytes) used for DRR of flow buckets */ ++ __u8 ecn; /* 0 - disable ECN, 1 - enable ECN */ ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++struct tc_nsscodel_xstats { ++ __u32 peak_queue_delay; /* Peak delay experienced by a dequeued packet */ ++ __u32 peak_drop_delay; /* Peak delay experienced by a dropped packet */ ++}; ++ ++/* NSSFQ_CODEL section */ ++ ++struct tc_nssfq_codel_xstats { ++ __u32 new_flow_count; /* Total number of new flows seen */ ++ __u32 new_flows_len; /* Current number of new flows */ ++ __u32 old_flows_len; /* Current number of old flows */ ++ __u32 ecn_mark; /* Number of packets marked with ECN */ ++ __u32 drop_overlimit; /* Number of packets dropped due to overlimit */ ++ __u32 maxpacket; /* The largest packet seen so far in the queue */ ++}; ++ ++/* NSSTBL section */ ++ ++enum { ++ TCA_NSSTBL_UNSPEC, ++ TCA_NSSTBL_PARMS, ++ __TCA_NSSTBL_MAX ++}; ++ ++#define TCA_NSSTBL_MAX (__TCA_NSSTBL_MAX - 1) ++ ++struct tc_nsstbl_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Limiting rate of TBF */ ++ __u32 peakrate; /* Maximum rate at which TBF is allowed to send */ ++ __u32 mtu; /* Max size of packet, or minumim burst size */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSPRIO section */ ++ ++#define TCA_NSSPRIO_MAX_BANDS 256 ++ ++enum { ++ TCA_NSSPRIO_UNSPEC, ++ TCA_NSSPRIO_PARMS, ++ __TCA_NSSPRIO_MAX ++}; ++ ++#define TCA_NSSPRIO_MAX (__TCA_NSSPRIO_MAX - 1) ++ ++struct tc_nssprio_qopt { ++ __u32 bands; /* Number of bands */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBF section */ ++ ++enum { ++ TCA_NSSBF_UNSPEC, ++ TCA_NSSBF_CLASS_PARMS, ++ TCA_NSSBF_QDISC_PARMS, ++ __TCA_NSSBF_MAX ++}; ++ ++#define TCA_NSSBF_MAX (__TCA_NSSBF_MAX - 1) ++ ++struct tc_nssbf_class_qopt { ++ __u32 burst; /* Maximum burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 mtu; /* MTU of the associated interface */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++}; ++ ++struct tc_nssbf_qopt { ++ __u16 defcls; /* Default class value */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWRR section */ ++ ++enum { ++ TCA_NSSWRR_UNSPEC, ++ TCA_NSSWRR_CLASS_PARMS, ++ TCA_NSSWRR_QDISC_PARMS, ++ __TCA_NSSWRR_MAX ++}; ++ ++#define TCA_NSSWRR_MAX (__TCA_NSSWRR_MAX - 1) ++ ++struct tc_nsswrr_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswrr_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSWFQ section */ ++ ++enum { ++ TCA_NSSWFQ_UNSPEC, ++ TCA_NSSWFQ_CLASS_PARMS, ++ TCA_NSSWFQ_QDISC_PARMS, ++ __TCA_NSSWFQ_MAX ++}; ++ ++#define TCA_NSSWFQ_MAX (__TCA_NSSWFQ_MAX - 1) ++ ++struct tc_nsswfq_class_qopt { ++ __u32 quantum; /* Weight associated to this class */ ++}; ++ ++struct tc_nsswfq_qopt { ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSHTB section */ ++ ++enum { ++ TCA_NSSHTB_UNSPEC, ++ TCA_NSSHTB_CLASS_PARMS, ++ TCA_NSSHTB_QDISC_PARMS, ++ __TCA_NSSHTB_MAX ++}; ++ ++#define TCA_NSSHTB_MAX (__TCA_NSSHTB_MAX - 1) ++ ++struct tc_nsshtb_class_qopt { ++ __u32 burst; /* Allowed burst size */ ++ __u32 rate; /* Allowed bandwidth for this class */ ++ __u32 cburst; /* Maximum burst size */ ++ __u32 crate; /* Maximum bandwidth for this class */ ++ __u32 quantum; /* Quantum allocation for DRR */ ++ __u32 priority; /* Priority value associated with this class */ ++ __u32 overhead; /* Overhead in bytes per packet */ ++}; ++ ++struct tc_nsshtb_qopt { ++ __u32 r2q; /* Rate to quantum ratio */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++ ++/* NSSBLACKHOLE section */ ++ ++enum { ++ TCA_NSSBLACKHOLE_UNSPEC, ++ TCA_NSSBLACKHOLE_PARMS, ++ __TCA_NSSBLACKHOLE_MAX ++}; ++ ++#define TCA_NSSBLACKHOLE_MAX (__TCA_NSSBLACKHOLE_MAX - 1) ++ ++struct tc_nssblackhole_qopt { ++ __u8 set_default; /* Sets qdisc to be the default qdisc for enqueue */ ++ __u8 accel_mode; /* Dictates which data plane offloads the qdisc */ ++}; ++/* QCA NSS Clients Support - End */ + #endif +--- a/net/sched/sch_api.c ++++ b/net/sched/sch_api.c +@@ -314,6 +314,7 @@ struct Qdisc *qdisc_lookup(struct net_de + out: + return q; + } ++EXPORT_SYMBOL(qdisc_lookup); + + struct Qdisc *qdisc_lookup_rcu(struct net_device *dev, u32 handle) + { +@@ -2389,4 +2390,26 @@ static int __init pktsched_init(void) + return 0; + } + ++/* QCA NSS Qdisc Support - Start */ ++bool tcf_destroy(struct tcf_proto *tp, bool force) ++{ ++ tp->ops->destroy(tp, force, NULL); ++ module_put(tp->ops->owner); ++ kfree_rcu(tp, rcu); ++ ++ return true; ++} ++ ++void tcf_destroy_chain(struct tcf_proto __rcu **fl) ++{ ++ struct tcf_proto *tp; ++ ++ while ((tp = rtnl_dereference(*fl)) != NULL) { ++ RCU_INIT_POINTER(*fl, tp->next); ++ tcf_destroy(tp, true); ++ } ++} ++EXPORT_SYMBOL(tcf_destroy_chain); ++/* QCA NSS Qdisc Support - End */ ++ + subsys_initcall(pktsched_init); +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -1069,6 +1069,7 @@ static void __qdisc_destroy(struct Qdisc + + call_rcu(&qdisc->rcu, qdisc_free_cb); + } ++EXPORT_SYMBOL(qdisc_destroy); + + void qdisc_destroy(struct Qdisc *qdisc) + { +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -94,6 +94,7 @@ struct Qdisc { + #define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */ + #define TCQ_F_NOLOCK 0x100 /* qdisc does not require locking */ + #define TCQ_F_OFFLOADED 0x200 /* qdisc is offloaded to HW */ ++#define TCQ_F_NSS 0x1000 /* NSS qdisc flag. */ + u32 limit; + const struct Qdisc_ops *ops; + struct qdisc_size_table __rcu *stab; +@@ -751,6 +752,42 @@ static inline bool skb_skip_tc_classify( + return false; + } + ++/* QCA NSS Qdisc Support - Start */ ++/* ++ * 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; ++} ++/* QCA NSS Qdisc Support - End */ ++ + /* 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 +1360,9 @@ static inline void qdisc_synchronize(con + msleep(1); + } + ++/* QCA NSS Qdisc Support - Start */ ++void qdisc_destroy(struct Qdisc *qdisc); ++void tcf_destroy_chain(struct tcf_proto __rcu **fl); ++/* QCA NSS Qdisc Support - End */ ++ + #endif +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -764,6 +764,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 +@@ -945,6 +946,9 @@ struct sk_buff { + __u8 tc_at_ingress:1; /* See TC_AT_INGRESS_MASK */ + __u8 tc_skip_classify:1; + #endif ++#ifdef CONFIG_NET_CLS_ACT ++ __u8 tc_skip_classify_offload:1; /* QCA NSS Qdisc Support */ ++#endif + __u8 remcsum_offload:1; + __u8 csum_complete_sw:1; + __u8 csum_level:2; diff --git a/target/linux/qualcommax/patches-6.6/0603-2-qca-nss-clients-add-l2tp-support.patch b/target/linux/qualcommax/patches-6.6/0603-2-qca-nss-clients-add-l2tp-support.patch new file mode 100644 index 00000000000000..7fa9184df25155 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-2-qca-nss-clients-add-l2tp-support.patch @@ -0,0 +1,46 @@ +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -398,6 +398,31 @@ err_tlock: + } + EXPORT_SYMBOL_GPL(l2tp_session_register); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, ++ struct l2tp_session *session, ++ struct l2tp_stats *stats) ++{ ++ atomic_long_add(atomic_long_read(&stats->rx_packets), ++ &tunnel->stats.rx_packets); ++ atomic_long_add(atomic_long_read(&stats->rx_bytes), ++ &tunnel->stats.rx_bytes); ++ atomic_long_add(atomic_long_read(&stats->tx_packets), ++ &tunnel->stats.tx_packets); ++ atomic_long_add(atomic_long_read(&stats->tx_bytes), ++ &tunnel->stats.tx_bytes); ++ ++ atomic_long_add(atomic_long_read(&stats->rx_packets), ++ &session->stats.rx_packets); ++ atomic_long_add(atomic_long_read(&stats->rx_bytes), ++ &session->stats.rx_bytes); ++ atomic_long_add(atomic_long_read(&stats->tx_packets), ++ &session->stats.tx_packets); ++ atomic_long_add(atomic_long_read(&stats->tx_bytes), ++ &session->stats.tx_bytes); ++} ++EXPORT_SYMBOL_GPL(l2tp_stats_update); ++ ++ + /***************************************************************************** + * Receive data handling + *****************************************************************************/ +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -232,6 +232,9 @@ struct l2tp_session *l2tp_session_get_nt + struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, + const char *ifname); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, ++ struct l2tp_stats *stats); ++ + /* Tunnel and session lifetime management. + * Creation of a new instance is a two-step process: create, then register. + * Destruction is triggered using the *_delete functions, and completes asynchronously. diff --git a/target/linux/qualcommax/patches-6.6/0603-3-qca-nss-clients-add-PPTP-support.patch b/target/linux/qualcommax/patches-6.6/0603-3-qca-nss-clients-add-PPTP-support.patch new file mode 100644 index 00000000000000..5fb9917bc9067e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-3-qca-nss-clients-add-PPTP-support.patch @@ -0,0 +1,478 @@ +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -36,6 +36,7 @@ struct pptp_opt { + u32 ack_sent, ack_recv; + u32 seq_sent, seq_recv; + int ppp_flags; ++ bool pptp_offload_mode; + }; + #include + +@@ -100,8 +101,40 @@ struct pppoe_channel_ops { + int (*get_addressing)(struct ppp_channel *, struct pppoe_opt *); + }; + ++/* PPTP client callback */ ++typedef int (*pptp_gre_seq_offload_callback_t)(struct sk_buff *skb, ++ struct net_device *pptp_dev); ++ + /* Return PPPoE channel specific addressing information */ + extern int pppoe_channel_addressing_get(struct ppp_channel *chan, + struct pppoe_opt *addressing); + ++/* Lookup PPTP session info and return PPTP session using sip, dip and local call id */ ++extern int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr); ++ ++/* Lookup PPTP session info and return PPTP session using dip and peer call id */ ++extern int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Return PPTP session information given the channel */ ++extern void pptp_channel_addressing_get(struct pptp_opt *opt, ++ struct ppp_channel *chan); ++ ++/* Enable the PPTP session offload flag */ ++extern int pptp_session_enable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Disable the PPTP session offload flag */ ++extern int pptp_session_disable_offload_mode(__be16 peer_call_id, ++ __be32 peer_ip_addr); ++ ++/* Register the PPTP GRE packets sequence number offload callback */ ++extern int ++pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_client_cb); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++extern void pptp_unregister_gre_seq_offload_callback(void); ++ + #endif /* !(__LINUX_IF_PPPOX_H) */ +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -2973,6 +2973,20 @@ char *ppp_dev_name(struct ppp_channel *c + return name; + } + ++/* Return the PPP net device index */ ++int ppp_dev_index(struct ppp_channel *chan) ++{ ++ struct channel *pch = chan->ppp; ++ int ifindex = 0; ++ ++ if (pch) { ++ read_lock_bh(&pch->upl); ++ if (pch->ppp && pch->ppp->dev) ++ ifindex = pch->ppp->dev->ifindex; ++ read_unlock_bh(&pch->upl); ++ } ++ return ifindex; ++} + + /* + * Disconnect a channel from the generic layer. +@@ -3681,6 +3695,28 @@ void ppp_update_stats(struct net_device + ppp_recv_unlock(ppp); + } + ++/* Returns true if Compression is enabled on PPP device ++ */ ++bool ppp_is_cp_enabled(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ bool flag = false; ++ ++ if (!dev) ++ return false; ++ ++ if (dev->type != ARPHRD_PPP) ++ return false; ++ ++ ppp = netdev_priv(dev); ++ ppp_lock(ppp); ++ flag = !!(ppp->xstate & SC_COMP_RUN) || !!(ppp->rstate & SC_DECOMP_RUN); ++ ppp_unlock(ppp); ++ ++ return flag; ++} ++EXPORT_SYMBOL(ppp_is_cp_enabled); ++ + /* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if + * the device is not PPP. + */ +@@ -3872,6 +3908,7 @@ EXPORT_SYMBOL(ppp_unregister_channel); + EXPORT_SYMBOL(ppp_channel_index); + EXPORT_SYMBOL(ppp_unit_number); + EXPORT_SYMBOL(ppp_dev_name); ++EXPORT_SYMBOL(ppp_dev_index); + EXPORT_SYMBOL(ppp_input); + EXPORT_SYMBOL(ppp_input_error); + EXPORT_SYMBOL(ppp_output_wakeup); +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -84,6 +84,9 @@ extern void ppp_unregister_channel(struc + /* Get the channel number for a channel */ + extern int ppp_channel_index(struct ppp_channel *); + ++/* Get the device index associated with a channel, or 0, if none */ ++extern int ppp_dev_index(struct ppp_channel *); ++ + /* Get the unit number associated with a channel, or -1 if none */ + extern int ppp_unit_number(struct ppp_channel *); + +@@ -116,6 +119,7 @@ extern int ppp_hold_channels(struct net_ + /* Test if ppp xmit lock is locked */ + extern bool ppp_is_xmit_locked(struct net_device *dev); + ++bool ppp_is_cp_enabled(struct net_device *dev); + /* Test if the ppp device is a multi-link ppp device */ + extern int ppp_is_multilink(struct net_device *dev); + +--- a/drivers/net/ppp/pptp.c ++++ b/drivers/net/ppp/pptp.c +@@ -50,6 +50,8 @@ static struct proto pptp_sk_proto __read + static const struct ppp_channel_ops pptp_chan_ops; + static const struct proto_ops pptp_ops; + ++static pptp_gre_seq_offload_callback_t __rcu pptp_gre_offload_xmit_cb; ++ + static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) + { + struct pppox_sock *sock; +@@ -91,6 +93,79 @@ static int lookup_chan_dst(u16 call_id, + return i < MAX_CALLID; + } + ++/* Search a pptp session based on local call id, local and remote ip address */ ++static int lookup_session_src(struct pptp_opt *opt, u16 call_id, __be32 daddr, __be32 saddr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.src_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == daddr && ++ sock->proto.pptp.src_addr.sin_addr.s_addr == saddr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Search a pptp session based on peer call id and peer ip address */ ++static int lookup_session_dst(struct pptp_opt *opt, u16 call_id, __be32 d_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == d_addr) { ++ sock_hold(sk_pppox(sock)); ++ memcpy(opt, &sock->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* If offload mode set then this function sends all packets to ++ * offload module instead of network stack ++ */ ++static int pptp_client_skb_xmit(struct sk_buff *skb, ++ struct net_device *pptp_dev) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ int ret; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (!pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ ret = pptp_gre_offload_cb_f(skb, pptp_dev); ++ rcu_read_unlock(); ++ return ret; ++} ++ + static int add_chan(struct pppox_sock *sock, + struct pptp_addr *sa) + { +@@ -136,7 +211,7 @@ static struct rtable *pptp_route_output( + struct net *net; + + net = sock_net(sk); +- flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 0, ++ flowi4_init_output(fl4, 0, sk->sk_mark, 0, + RT_SCOPE_UNIVERSE, IPPROTO_GRE, 0, + po->proto.pptp.dst_addr.sin_addr.s_addr, + po->proto.pptp.src_addr.sin_addr.s_addr, +@@ -163,8 +238,11 @@ static int pptp_xmit(struct ppp_channel + + struct rtable *rt; + struct net_device *tdev; ++ struct net_device *pptp_dev; + struct iphdr *iph; + int max_headroom; ++ int pptp_ifindex; ++ int ret; + + if (sk_pppox(po)->sk_state & PPPOX_DEAD) + goto tx_error; +@@ -258,7 +336,32 @@ static int pptp_xmit(struct ppp_channel + ip_select_ident(net, skb, NULL); + ip_send_check(iph); + +- ip_local_out(net, skb->sk, skb); ++ pptp_ifindex = ppp_dev_index(chan); ++ ++ /* set incoming interface as the ppp interface */ ++ if (skb->skb_iif) ++ skb->skb_iif = pptp_ifindex; ++ ++ /* If the PPTP GRE seq number offload module is not enabled yet ++ * then sends all PPTP GRE packets through linux network stack ++ */ ++ if (!opt->pptp_offload_mode) { ++ ip_local_out(net, skb->sk, skb); ++ return 1; ++ } ++ ++ pptp_dev = dev_get_by_index(&init_net, pptp_ifindex); ++ if (!pptp_dev) ++ goto tx_error; ++ ++ /* If PPTP offload module is enabled then forward all PPTP GRE ++ * packets to PPTP GRE offload module ++ */ ++ ret = pptp_client_skb_xmit(skb, pptp_dev); ++ dev_put(pptp_dev); ++ if (ret < 0) ++ goto tx_error; ++ + return 1; + + tx_error: +@@ -314,6 +417,13 @@ static int pptp_rcv_core(struct sock *sk + goto drop; + + payload = skb->data + headersize; ++ ++ /* If offload is enabled, we expect the offload module ++ * to handle PPTP GRE sequence number checks ++ */ ++ if (opt->pptp_offload_mode) ++ goto allow_packet; ++ + /* check for expected sequence number */ + if (seq < opt->seq_recv + 1 || WRAPPED(opt->seq_recv, seq)) { + if ((payload[0] == PPP_ALLSTATIONS) && (payload[1] == PPP_UI) && +@@ -371,6 +481,7 @@ static int pptp_rcv(struct sk_buff *skb) + if (po) { + skb_dst_drop(skb); + nf_reset_ct(skb); ++ skb->skb_iif = ppp_dev_index(&po->chan); + return sk_receive_skb(sk_pppox(po), skb, 0); + } + drop: +@@ -473,7 +584,7 @@ static int pptp_connect(struct socket *s + + opt->dst_addr = sp->sa_addr.pptp; + sk->sk_state |= PPPOX_CONNECTED; +- ++ opt->pptp_offload_mode = false; + end: + release_sock(sk); + return error; +@@ -603,9 +714,169 @@ static int pptp_ppp_ioctl(struct ppp_cha + return err; + } + ++/* pptp_channel_addressing_get() ++ * Return PPTP channel specific addressing information. ++ */ ++void pptp_channel_addressing_get(struct pptp_opt *opt, struct ppp_channel *chan) ++{ ++ struct sock *sk; ++ struct pppox_sock *po; ++ ++ if (!opt) ++ return; ++ ++ sk = (struct sock *)chan->private; ++ if (!sk) ++ return; ++ ++ sock_hold(sk); ++ ++ /* This is very unlikely, but check the socket is connected state */ ++ if (unlikely(sock_flag(sk, SOCK_DEAD) || ++ !(sk->sk_state & PPPOX_CONNECTED))) { ++ sock_put(sk); ++ return; ++ } ++ ++ po = pppox_sk(sk); ++ memcpy(opt, &po->proto.pptp, sizeof(struct pptp_opt)); ++ sock_put(sk); ++} ++EXPORT_SYMBOL(pptp_channel_addressing_get); ++ ++/* pptp_session_find() ++ * Search and return a PPTP session info based on peer callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find(struct pptp_opt *opt, __be16 peer_call_id, ++ __be32 peer_ip_addr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_dst(opt, ntohs(peer_call_id), peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_find); ++ ++/* pptp_session_find_by_src_callid() ++ * Search and return a PPTP session info based on src callid and IP ++ * address. The function accepts the parameters in network byte order. ++ */ ++int pptp_session_find_by_src_callid(struct pptp_opt *opt, __be16 src_call_id, ++ __be32 daddr, __be32 saddr) ++{ ++ if (!opt) ++ return -EINVAL; ++ ++ return lookup_session_src(opt, ntohs(src_call_id), daddr, saddr); ++} ++EXPORT_SYMBOL(pptp_session_find_by_src_callid); ++ ++ /* Function to change the offload mode true/false for a PPTP session */ ++static int pptp_set_offload_mode(bool accel_mode, ++ __be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ struct pppox_sock *sock; ++ int i = 1; ++ ++ rcu_read_lock(); ++ for_each_set_bit_from(i, callid_bitmap, MAX_CALLID) { ++ sock = rcu_dereference(callid_sock[i]); ++ if (!sock) ++ continue; ++ ++ if (sock->proto.pptp.dst_addr.call_id == peer_call_id && ++ sock->proto.pptp.dst_addr.sin_addr.s_addr == peer_ip_addr) { ++ sock_hold(sk_pppox(sock)); ++ sock->proto.pptp.pptp_offload_mode = accel_mode; ++ sock_put(sk_pppox(sock)); ++ rcu_read_unlock(); ++ return 0; ++ } ++ } ++ rcu_read_unlock(); ++ return -EINVAL; ++} ++ ++/* Enable the PPTP session offload flag */ ++int pptp_session_enable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(true, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_enable_offload_mode); ++ ++/* Disable the PPTP session offload flag */ ++int pptp_session_disable_offload_mode(__be16 peer_call_id, __be32 peer_ip_addr) ++{ ++ return pptp_set_offload_mode(false, peer_call_id, peer_ip_addr); ++} ++EXPORT_SYMBOL(pptp_session_disable_offload_mode); ++ ++/* Register the offload callback function on behalf of the module which ++ * will own the sequence and acknowledgment number updates for all ++ * PPTP GRE packets. All PPTP GRE packets are then transmitted to this ++ * module after encapsulation in order to ensure the correct seq/ack ++ * fields are set in the packets before transmission. This is required ++ * when PPTP flows are offloaded to acceleration engines, in-order to ++ * ensure consistency in sequence and ack numbers between PPTP control ++ * (PPP LCP) and data packets ++ */ ++int pptp_register_gre_seq_offload_callback(pptp_gre_seq_offload_callback_t ++ pptp_gre_offload_cb) ++{ ++ pptp_gre_seq_offload_callback_t pptp_gre_offload_cb_f; ++ ++ rcu_read_lock(); ++ pptp_gre_offload_cb_f = rcu_dereference(pptp_gre_offload_xmit_cb); ++ ++ if (pptp_gre_offload_cb_f) { ++ rcu_read_unlock(); ++ return -1; ++ } ++ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, pptp_gre_offload_cb); ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL(pptp_register_gre_seq_offload_callback); ++ ++/* Unregister the PPTP GRE packets sequence number offload callback */ ++void pptp_unregister_gre_seq_offload_callback(void) ++{ ++ rcu_assign_pointer(pptp_gre_offload_xmit_cb, NULL); ++} ++EXPORT_SYMBOL(pptp_unregister_gre_seq_offload_callback); ++ ++/* pptp_hold_chan() */ ++static void pptp_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/* pptp_release_chan() */ ++static void pptp_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/* pptp_get_channel_protocol() ++ * Return the protocol type of the PPTP over PPP protocol ++ */ ++static int pptp_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_PPTP; ++} ++ + static const struct ppp_channel_ops pptp_chan_ops = { + .start_xmit = pptp_xmit, + .ioctl = pptp_ppp_ioctl, ++ .get_channel_protocol = pptp_get_channel_protocol, ++ .hold = pptp_hold_chan, ++ .release = pptp_release_chan, + }; + + static struct proto pptp_sk_proto __read_mostly = { diff --git a/target/linux/qualcommax/patches-6.6/0603-4-qca-nss-clients-add-iptunnel-support.patch b/target/linux/qualcommax/patches-6.6/0603-4-qca-nss-clients-add-iptunnel-support.patch new file mode 100644 index 00000000000000..c9fc33686480ec --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-4-qca-nss-clients-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 +@@ -558,4 +558,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 +@@ -2411,6 +2411,26 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Client Support - Start */ ++/* ++ * Update offload stats ++ */ ++void ip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ip6_update_offload_stats); ++/* QCA NSS Client Support - End */ ++ + struct net *ip6_tnl_get_link_net(const struct net_device *dev) + { + struct ip6_tnl *tunnel = netdev_priv(dev); +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1733,6 +1733,23 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ipip6_update_offload_stats); ++/* QCA NSS Clients Support - End */ ++ + static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, diff --git a/target/linux/qualcommax/patches-6.6/0603-5-qca-nss-clients-add-vxlan-support.patch b/target/linux/qualcommax/patches-6.6/0603-5-qca-nss-clients-add-vxlan-support.patch new file mode 100644 index 00000000000000..7d05fd1aff619c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-5-qca-nss-clients-add-vxlan-support.patch @@ -0,0 +1,103 @@ +--- a/drivers/net/vxlan/vxlan_core.c ++++ b/drivers/net/vxlan/vxlan_core.c +@@ -29,6 +29,20 @@ + #include + #include + ++ATOMIC_NOTIFIER_HEAD(vxlan_fdb_notifier_list); ++ ++void vxlan_fdb_register_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_register(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_register_notify); ++ ++void vxlan_fdb_unregister_notify(struct notifier_block *nb) ++{ ++ atomic_notifier_chain_unregister(&vxlan_fdb_notifier_list, nb); ++} ++EXPORT_SYMBOL(vxlan_fdb_unregister_notify); ++ + #if IS_ENABLED(CONFIG_IPV6) + #include + #include +@@ -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, +@@ -2581,6 +2612,9 @@ void vxlan_xmit_one(struct sk_buff *skb, + goto out_unlock; + } + ++ /* Reset the skb_iif to Tunnels interface index */ ++ skb->skb_iif = dev->ifindex; ++ + tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); + err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), +@@ -2652,6 +2686,9 @@ void vxlan_xmit_one(struct sk_buff *skb, + if (err < 0) + goto tx_error; + ++ /* Reset the skb_iif to Tunnels interface index */ ++ skb->skb_iif = dev->ifindex; ++ + udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev, + &local_ip.sin6.sin6_addr, + &dst->sin6.sin6_addr, tos, ttl, +--- a/include/net/vxlan.h ++++ b/include/net/vxlan.h +@@ -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); + diff --git a/target/linux/qualcommax/patches-6.6/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch b/target/linux/qualcommax/patches-6.6/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch new file mode 100644 index 00000000000000..4032eb3c227134 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-6-qca-nss-clients-add-l2tp-offloading-support.patch @@ -0,0 +1,368 @@ +--- a/include/linux/ppp_channel.h ++++ b/include/linux/ppp_channel.h +@@ -61,6 +61,51 @@ struct ppp_channel { + }; + + #ifdef __KERNEL__ ++/* Call this to obtain the underlying protocol of the PPP channel, ++ * e.g. PX_PROTO_OE ++ */ ++extern int ppp_channel_get_protocol(struct ppp_channel *); ++ ++/* Call this to hold a channel */ ++extern bool ppp_channel_hold(struct ppp_channel *); ++ ++/* Call this to release a hold you have upon a channel */ ++extern void ppp_channel_release(struct ppp_channel *); ++ ++/* Release hold on PPP channels */ ++extern void ppp_release_channels(struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if ppp xmit lock is locked */ ++extern bool ppp_is_xmit_locked(struct net_device *dev); ++ ++/* Call this get protocol version */ ++extern int ppp_channel_get_proto_version(struct ppp_channel *); ++ ++/* Get the device index associated with a channel, or 0, if none */ ++extern int ppp_dev_index(struct ppp_channel *); ++ ++/* Hold PPP channels for the PPP device */ ++extern int ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++extern int __ppp_hold_channels(struct net_device *dev, ++ struct ppp_channel *channels[], ++ unsigned int chan_sz); ++ ++/* Test if the ppp device is a multi-link ppp device */ ++extern int ppp_is_multilink(struct net_device *dev); ++extern int __ppp_is_multilink(struct net_device *dev); ++ ++/* Update statistics of the PPP net_device by incrementing related ++ * statistics field value with corresponding parameter ++ */ ++extern void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, ++ unsigned long rx_bytes, unsigned long tx_packets, ++ unsigned long tx_bytes, unsigned long rx_errors, ++ unsigned long tx_errors, unsigned long rx_dropped, ++ unsigned long tx_dropped); ++ + /* Called by the channel when it can send some more data. */ + extern void ppp_output_wakeup(struct ppp_channel *); + +@@ -148,5 +193,17 @@ extern void ppp_update_stats(struct net_ + * that ppp_unregister_channel returns. + */ + ++/* QCA NSS Clients Support - Start */ ++/* PPP channel connection event types */ ++#define PPP_CHANNEL_DISCONNECT 0 ++#define PPP_CHANNEL_CONNECT 1 ++ ++/* Register the PPP channel connect notifier */ ++extern void ppp_channel_connection_register_notify(struct notifier_block *nb); ++ ++/* Unregister the PPP channel connect notifier */ ++extern void ppp_channel_connection_unregister_notify(struct notifier_block *nb); ++/* QCA NSS Clients Support - End */ ++ + #endif /* __KERNEL__ */ + #endif +--- a/include/linux/if_pppol2tp.h ++++ b/include/linux/if_pppol2tp.h +@@ -12,4 +12,30 @@ + #include + #include + ++/* QCA NSS ECM support - Start */ ++/* ++ * Holds L2TP channel info ++ */ ++struct pppol2tp_common_addr { ++ int tunnel_version; /* v2 or v3 */ ++ __u32 local_tunnel_id, remote_tunnel_id; /* tunnel id */ ++ __u32 local_session_id, remote_session_id; /* session id */ ++ struct sockaddr_in local_addr, remote_addr; /* ip address and port */ ++}; ++ ++/* ++ * L2TP channel operations ++ */ ++struct pppol2tp_channel_ops { ++ struct ppp_channel_ops ops; /* ppp channel ops */ ++}; ++ ++/* ++ * exported function which calls pppol2tp channel's get addressing ++ * function ++ */ ++extern int pppol2tp_channel_addressing_get(struct ppp_channel *, ++ struct pppol2tp_common_addr *); ++/* QCA NSS ECM support - End */ ++ + #endif +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -123,9 +123,17 @@ struct pppol2tp_session { + }; + + static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb); +- +-static const struct ppp_channel_ops pppol2tp_chan_ops = { +- .start_xmit = pppol2tp_xmit, ++static int pppol2tp_get_channel_protocol(struct ppp_channel *); ++static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *); ++static void pppol2tp_hold_chan(struct ppp_channel *); ++static void pppol2tp_release_chan(struct ppp_channel *); ++ ++static const struct pppol2tp_channel_ops pppol2tp_chan_ops = { ++ .ops.start_xmit = pppol2tp_xmit, ++ .ops.get_channel_protocol = pppol2tp_get_channel_protocol, ++ .ops.get_channel_protocol_ver = pppol2tp_get_channel_protocol_ver, ++ .ops.hold = pppol2tp_hold_chan, ++ .ops.release = pppol2tp_release_chan, + }; + + static const struct proto_ops pppol2tp_ops; +@@ -373,6 +381,13 @@ static int pppol2tp_xmit(struct ppp_chan + skb->data[0] = PPP_ALLSTATIONS; + skb->data[1] = PPP_UI; + ++ /* QCA NSS ECM support - start */ ++ /* set incoming interface as the ppp interface */ ++ if ((skb->protocol == htons(ETH_P_IP)) || ++ (skb->protocol == htons(ETH_P_IPV6))) ++ skb->skb_iif = ppp_dev_index(chan); ++ /* QCA NSS ECM support - End */ ++ + local_bh_disable(); + l2tp_xmit_skb(session, skb); + local_bh_enable(); +@@ -818,7 +833,7 @@ static int pppol2tp_connect(struct socke + po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ; + + po->chan.private = sk; +- po->chan.ops = &pppol2tp_chan_ops; ++ po->chan.ops = (struct ppp_channel_ops *)&pppol2tp_chan_ops.ops; + po->chan.mtu = pppol2tp_tunnel_mtu(tunnel); + + error = ppp_register_net_channel(sock_net(sk), &po->chan); +@@ -1732,6 +1747,109 @@ static void __exit pppol2tp_exit(void) + unregister_pernet_device(&pppol2tp_net_ops); + } + ++/* QCA NSS ECM support - Start */ ++/* pppol2tp_hold_chan() */ ++static void pppol2tp_hold_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_hold(sk); ++} ++ ++/* pppol2tp_release_chan() */ ++static void pppol2tp_release_chan(struct ppp_channel *chan) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ ++ sock_put(sk); ++} ++ ++/* pppol2tp_get_channel_protocol() ++ * Return the protocol type of the L2TP over PPP protocol ++ */ ++static int pppol2tp_get_channel_protocol(struct ppp_channel *chan) ++{ ++ return PX_PROTO_OL2TP; ++} ++ ++/* pppol2tp_get_channel_protocol_ver() ++ * Return the protocol version of the L2TP over PPP protocol ++ */ ++static int pppol2tp_get_channel_protocol_ver(struct ppp_channel *chan) ++{ ++ struct sock *sk; ++ struct l2tp_session *session; ++ struct l2tp_tunnel *tunnel; ++ int version = 0; ++ ++ if (chan && chan->private) ++ sk = (struct sock *)chan->private; ++ else ++ return -1; ++ ++ /* Get session and tunnel contexts from the socket */ ++ session = pppol2tp_sock_to_session(sk); ++ if (!session) ++ return -1; ++ ++ tunnel = session->tunnel; ++ if (!tunnel) { ++ sock_put(sk); ++ return -1; ++ } ++ ++ version = tunnel->version; ++ ++ sock_put(sk); ++ ++ return version; ++} ++ ++/* pppol2tp_get_addressing() */ ++static int pppol2tp_get_addressing(struct ppp_channel *chan, ++ struct pppol2tp_common_addr *addr) ++{ ++ struct sock *sk = (struct sock *)chan->private; ++ struct l2tp_session *session; ++ struct l2tp_tunnel *tunnel; ++ struct inet_sock *isk = NULL; ++ int err = -ENXIO; ++ ++ /* Get session and tunnel contexts from the socket */ ++ session = pppol2tp_sock_to_session(sk); ++ if (!session) ++ return err; ++ ++ tunnel = session->tunnel; ++ if (!tunnel) { ++ sock_put(sk); ++ return err; ++ } ++ isk = inet_sk(tunnel->sock); ++ ++ addr->local_tunnel_id = tunnel->tunnel_id; ++ addr->remote_tunnel_id = tunnel->peer_tunnel_id; ++ addr->local_session_id = session->session_id; ++ addr->remote_session_id = session->peer_session_id; ++ ++ addr->local_addr.sin_port = isk->inet_sport; ++ addr->remote_addr.sin_port = isk->inet_dport; ++ addr->local_addr.sin_addr.s_addr = isk->inet_saddr; ++ addr->remote_addr.sin_addr.s_addr = isk->inet_daddr; ++ ++ sock_put(sk); ++ return 0; ++} ++ ++/* pppol2tp_channel_addressing_get() */ ++int pppol2tp_channel_addressing_get(struct ppp_channel *chan, ++ struct pppol2tp_common_addr *addr) ++{ ++ return pppol2tp_get_addressing(chan, addr); ++} ++EXPORT_SYMBOL(pppol2tp_channel_addressing_get); ++/* QCA NSS ECM support - End */ ++ + module_init(pppol2tp_init); + module_exit(pppol2tp_exit); + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -3743,6 +3743,32 @@ int ppp_is_multilink(struct net_device * + } + EXPORT_SYMBOL(ppp_is_multilink); + ++/* __ppp_is_multilink() ++ * Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 ++ * if the device is not PPP. Caller should acquire ppp_lock before calling ++ * this function ++ */ ++int __ppp_is_multilink(struct net_device *dev) ++{ ++ struct ppp *ppp; ++ unsigned int flags; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ flags = ppp->flags; ++ ++ if (flags & SC_MULTILINK) ++ return 1; ++ ++ return 0; ++} ++EXPORT_SYMBOL(__ppp_is_multilink); ++ + /* ppp_channel_get_protocol() + * Call this to obtain the underlying protocol of the PPP channel, + * e.g. PX_PROTO_OE +@@ -3881,6 +3907,59 @@ int ppp_hold_channels(struct net_device + } + EXPORT_SYMBOL(ppp_hold_channels); + ++/* __ppp_hold_channels() ++ * Returns the PPP channels of the PPP device, storing each one into ++ * channels[]. ++ * ++ * channels[] has chan_sz elements. ++ * This function returns the number of channels stored, up to chan_sz. ++ * It will return < 0 if the device is not PPP. ++ * ++ * You MUST release the channels using ppp_release_channels(). ++ */ ++int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], ++ unsigned int chan_sz) ++{ ++ struct ppp *ppp; ++ int c; ++ struct channel *pch; ++ ++ if (!dev) ++ return -1; ++ ++ if (dev->type != ARPHRD_PPP) ++ return -1; ++ ++ ppp = netdev_priv(dev); ++ ++ c = 0; ++ list_for_each_entry(pch, &ppp->channels, clist) { ++ struct ppp_channel *chan; ++ ++ if (!pch->chan) { ++ /* Channel is going / gone away */ ++ continue; ++ } ++ ++ if (c == chan_sz) { ++ /* No space to record channel */ ++ return c; ++ } ++ ++ /* Hold the channel, if supported */ ++ chan = pch->chan; ++ if (!chan->ops->hold) ++ continue; ++ ++ chan->ops->hold(chan); ++ ++ /* Record the channel */ ++ channels[c++] = chan; ++ } ++ return c; ++} ++EXPORT_SYMBOL(__ppp_hold_channels); ++ + /* ppp_release_channels() + * Releases channels + */ +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -235,6 +235,9 @@ struct l2tp_session *l2tp_session_get_by + void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, + struct l2tp_stats *stats); + ++void l2tp_stats_update(struct l2tp_tunnel *tunnel, struct l2tp_session *session, ++ struct l2tp_stats *stats); ++ + /* Tunnel and session lifetime management. + * Creation of a new instance is a two-step process: create, then register. + * Destruction is triggered using the *_delete functions, and completes asynchronously. diff --git a/target/linux/qualcommax/patches-6.6/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch b/target/linux/qualcommax/patches-6.6/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch new file mode 100644 index 00000000000000..e4ed49ea4c21fb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-7-qca-nss-clients-iptunnel-lock-this-cpu.patch @@ -0,0 +1,22 @@ +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -2417,7 +2417,7 @@ nla_put_failure: + */ + void ip6_update_offload_stats(struct net_device *dev, void *ptr) + { +- struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); + const struct pcpu_sw_netstats *offload_stats = + (struct pcpu_sw_netstats *)ptr; + +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1736,7 +1736,7 @@ nla_put_failure: + /* QCA NSS Clients Support - Start */ + void ipip6_update_offload_stats(struct net_device *dev, void *ptr) + { +- struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats); + const struct pcpu_sw_netstats *offload_stats = + (struct pcpu_sw_netstats *)ptr; + diff --git a/target/linux/qualcommax/patches-6.6/0603-8-qca-nss-clients-add-tls-mgr-support.patch b/target/linux/qualcommax/patches-6.6/0603-8-qca-nss-clients-add-tls-mgr-support.patch new file mode 100644 index 00000000000000..0499e237f6be88 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0603-8-qca-nss-clients-add-tls-mgr-support.patch @@ -0,0 +1,24 @@ +--- /dev/null ++++ b/include/uapi/linux/tlshdr.h +@@ -0,0 +1,21 @@ ++#ifndef _UAPI_LINUX_TLSHDR_H ++#define _UAPI_LINUX_TLSHDR_H ++ ++#include ++ ++struct tlshdr { ++ __u8 type; ++ __be16 version; ++ __be16 len; ++} __attribute__((packed)); ++ ++#define TLSHDR_REC_TYPE_CCS 20 /* TLS packet is change cipher specification */ ++#define TLSHDR_REC_TYPE_ALERT 21 /* TLS packet is Alert */ ++#define TLSHDR_REC_TYPE_HANDSHAKE 22 /* TLS packet is Handshake */ ++#define TLSHDR_REC_TYPE_DATA 23 /* TLS packet is Application data */ ++ ++#define TLSHDR_VERSION_1_1 0x0302 /* TLS Header Version(tls 1.1) */ ++#define TLSHDR_VERSION_1_2 0x0303 /* TLS Header Version(tls 1.2) */ ++#define TLSHDR_VERSION_1_3 0x0304 /* TLS Header Version(tls 1.3) */ ++ ++#endif /* _UAPI_LINUX_TLSHDR_H */ diff --git a/target/linux/qualcommax/patches-6.6/0604-1-qca-add-mcs-support.patch b/target/linux/qualcommax/patches-6.6/0604-1-qca-add-mcs-support.patch new file mode 100644 index 00000000000000..4b87366d84966a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0604-1-qca-add-mcs-support.patch @@ -0,0 +1,876 @@ +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -258,4 +258,17 @@ extern br_get_dst_hook_t __rcu *br_get_d + extern struct net_device *br_fdb_bridge_dev_get_and_hold(struct net_bridge *br); + /* QCA NSS bridge-mgr support - End */ + ++/* QCA qca-mcs support - Start */ ++typedef struct net_bridge_port *br_get_dst_hook_t(const struct net_bridge_port *src, ++ struct sk_buff **skb); ++extern br_get_dst_hook_t __rcu *br_get_dst_hook; ++ ++typedef int (br_multicast_handle_hook_t)(const struct net_bridge_port *src, ++ struct sk_buff *skb); ++extern br_multicast_handle_hook_t __rcu *br_multicast_handle_hook; ++ ++typedef void (br_notify_hook_t)(int group, int event, const void *ptr); ++extern br_notify_hook_t __rcu *br_notify_hook; ++/* QCA qca-mcs support - End */ ++ + #endif +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -239,6 +239,8 @@ static void fdb_notify(struct net_bridge + kfree_skb(skb); + goto errout; + } ++ ++ __br_notify(RTNLGRP_NEIGH, type, fdb); /* QCA qca-mcs support */ + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + return; + errout: +@@ -305,6 +307,7 @@ struct net_bridge_fdb_entry *br_fdb_find + { + return fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); + } ++EXPORT_SYMBOL_GPL(br_fdb_find_rcu); /* QCA qca-mcs support */ + + /* When a static FDB entry is added, the mac address from the entry is + * added to the bridge private HW address list and all required ports +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -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); /* QCA qca-mcs support */ + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); + rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); + +@@ -2269,4 +2270,14 @@ struct nd_msg *br_is_nd_neigh_msg(struct + bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); + #define __br_get(__hook, __default, __args ...) \ + (__hook ? (__hook(__args)) : (__default)) /* QCA NSS ECM support */ ++ ++/* QCA qca-mcs support - Start */ ++static inline void __br_notify(int group, int type, const void *data) ++{ ++ br_notify_hook_t *notify_hook = rcu_dereference(br_notify_hook); ++ ++ if (notify_hook) ++ notify_hook(group, type, data); ++} ++/* QCA qca-mcs support - End */ + #endif +--- a/net/bridge/br_netlink.c ++++ b/net/bridge/br_netlink.c +@@ -656,6 +656,7 @@ void br_info_notify(int event, const str + kfree_skb(skb); + goto errout; + } ++ __br_notify(RTNLGRP_LINK, event, port); /* QCA qca-mcs support */ + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + return; + errout: +--- a/net/bridge/br.c ++++ b/net/bridge/br.c +@@ -472,6 +472,12 @@ static void __exit br_deinit(void) + br_fdb_fini(); + } + ++/* QCA qca-mcs support - Start */ ++/* Hook for bridge event notifications */ ++br_notify_hook_t __rcu *br_notify_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_notify_hook); ++/* QCA qca-mcs support - End */ ++ + module_init(br_init) + module_exit(br_deinit) + MODULE_LICENSE("GPL"); +--- a/net/bridge/br_device.c ++++ b/net/bridge/br_device.c +@@ -83,6 +83,13 @@ netdev_tx_t br_dev_xmit(struct sk_buff * + if (is_broadcast_ether_addr(dest)) { + br_flood(br, skb, BR_PKT_BROADCAST, false, true, vid); + } else if (is_multicast_ether_addr(dest)) { ++ /* QCA qca-mcs support - Start */ ++ br_multicast_handle_hook_t *multicast_handle_hook = ++ rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, NULL, skb)) ++ goto out; ++ /* QCA qca-mcs support - End */ ++ + if (unlikely(netpoll_tx_running(dev))) { + br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid); + goto out; +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -30,7 +30,17 @@ br_netif_receive_skb(struct net *net, st + return netif_receive_skb(skb); + } + +-static int br_pass_frame_up(struct sk_buff *skb, bool promisc) ++/* QCA qca-mcs support - Start */ ++/* Hook for external Multicast handler */ ++br_multicast_handle_hook_t __rcu *br_multicast_handle_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_multicast_handle_hook); ++ ++/* Hook for external forwarding logic */ ++br_get_dst_hook_t __rcu *br_get_dst_hook __read_mostly; ++EXPORT_SYMBOL_GPL(br_get_dst_hook); ++/* QCA qca-mcs support - End */ ++ ++int br_pass_frame_up(struct sk_buff *skb, bool promisc) + { + struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; + struct net_bridge *br = netdev_priv(brdev); +@@ -71,6 +81,7 @@ static int br_pass_frame_up(struct sk_bu + dev_net(indev), NULL, skb, indev, NULL, + br_netif_receive_skb); + } ++EXPORT_SYMBOL_GPL(br_pass_frame_up); /* QCA qca-mcs support */ + + /* note: already called with rcu_read_lock */ + int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb) +@@ -84,6 +95,11 @@ int br_handle_frame_finish(struct net *n + struct net_bridge_mcast *brmctx; + struct net_bridge_vlan *vlan; + struct net_bridge *br; ++ /* QCA qca-mcs support - Start */ ++ br_multicast_handle_hook_t *multicast_handle_hook; ++ struct net_bridge_port *pdst = NULL; ++ br_get_dst_hook_t *get_dst_hook = rcu_dereference(br_get_dst_hook); ++ /* QCA qca-mcs support - End */ + bool promisc; + u16 vid = 0; + u8 state; +@@ -180,6 +196,12 @@ int br_handle_frame_finish(struct net *n + + switch (pkt_type) { + case BR_PKT_MULTICAST: ++ /* QCA qca-mcs support - Start */ ++ multicast_handle_hook = rcu_dereference(br_multicast_handle_hook); ++ if (!__br_get(multicast_handle_hook, true, p, skb)) ++ goto out; ++ /* QCA qca-mcs support - End */ ++ + mdst = br_mdb_get(brmctx, skb, vid); + if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) && + br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) { +@@ -195,8 +217,15 @@ int br_handle_frame_finish(struct net *n + } + break; + case BR_PKT_UNICAST: +- dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); +- break; ++ /* QCA qca-mcs support - Start */ ++ pdst = __br_get(get_dst_hook, NULL, p, &skb); ++ if (pdst) { ++ if (!skb) ++ goto out; ++ } else { ++ /* QCA qca-mcs support - End */ ++ dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid); ++ } + default: + break; + } +@@ -211,6 +240,13 @@ int br_handle_frame_finish(struct net *n + dst->used = now; + br_forward(dst->dst, skb, local_rcv, false); + } else { ++ /* QCA qca-mcs support - Start */ ++ if (pdst) { ++ br_forward(pdst, skb, local_rcv, false); ++ goto out; ++ } ++ /* QCA qca-mcs support - End */ ++ + if (!mcast_hit) + br_flood(br, skb, pkt_type, local_rcv, false, vid); + else +--- a/include/linux/mroute.h ++++ b/include/linux/mroute.h +@@ -92,4 +92,44 @@ struct rtmsg; + int ipmr_get_route(struct net *net, struct sk_buff *skb, + __be32 saddr, __be32 daddr, + struct rtmsg *rtm, u32 portid); ++ ++/* QCA ECM qca-mcs support - Start */ ++#define IPMR_MFC_EVENT_UPDATE 1 ++#define IPMR_MFC_EVENT_DELETE 2 ++ ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ipmr_mfc_event_offload_callback_t)(__be32 origin, __be32 group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ u8 op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur to ++ * MFC. The callback is registered by offload modules ++ */ ++extern bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ipmr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list, given a multicast group and source ++ */ ++extern int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dst_cnt, u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out); ++/* QCA ECM qca-mcs support - End */ + #endif +--- a/include/linux/mroute6.h ++++ b/include/linux/mroute6.h +@@ -137,4 +137,47 @@ static inline int ip6mr_sk_ioctl(struct + return 1; + } + #endif ++ ++/* QCA qca-mcs support - Start */ ++#define IP6MR_MFC_EVENT_UPDATE 1 ++#define IP6MR_MFC_EVENT_DELETE 2 ++ ++/* ++ * Callback to registered modules in the event of updates to a multicast group ++ */ ++typedef void (*ip6mr_mfc_event_offload_callback_t)(struct in6_addr *origin, ++ struct in6_addr *group, ++ u32 max_dest_dev, ++ u32 dest_dev_idx[], ++ uint8_t op); ++ ++/* ++ * Register the callback used to inform offload modules when updates occur ++ * to MFC. The callback is registered by offload modules ++ */ ++extern bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb); ++ ++/* ++ * De-Register the callback used to inform offload modules when updates occur ++ * to MFC ++ */ ++extern void ip6mr_unregister_mfc_event_offload_callback(void); ++ ++/* ++ * Find the destination interface list given a multicast group and source ++ */ ++extern int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dst_cnt, ++ u32 dest_dev[]); ++ ++/* ++ * Out-of-band multicast statistics update for flows that are offloaded from ++ * Linux ++ */ ++extern int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, uint64_t pkts_in, ++ uint64_t bytes_in, uint64_t pkts_out, ++ uint64_t bytes_out); ++/* QCA qca-mcs support - End */ + #endif +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -89,6 +89,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* spinlock for offload */ ++static DEFINE_SPINLOCK(lock); /* QCA ECM qca-mcs support */ ++ + /* We return to original Alan's scheme. Hash table of resolved + * entries is changed only in process context and protected + * with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -112,6 +115,9 @@ static void mroute_netlink_event(struct + static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, __be32 origin, ++ __be32 mcastgrp); ++static ipmr_mfc_event_offload_callback_t __rcu ipmr_mfc_event_offload_callback; /* QCA ECM qca-mcs support */ + + #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES + #define ipmr_for_each_table(mrt, net) \ +@@ -223,6 +229,80 @@ static int ipmr_rule_fill(struct fib_rul + return 0; + } + ++/* QCA ECM qca-mcs support - Start */ ++/* ipmr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ipmr_sync_entry_update(struct mr_table *mrt, ++ struct mfc_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXVIFS]; ++ __be32 origin; ++ __be32 group; ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ origin = cache->mfc_origin; ++ group = cache->mfc_mcastgrp; ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ if (dest_if_count == MAXVIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, dest_if_count, dest_dev, ++ IPMR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ipmr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ipmr_sync_entry_delete(u32 origin, u32 group) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(group, origin, 0, NULL, IPMR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++/* QCA ECM qca-mcs support - End */ ++ + static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { + .family = RTNL_FAMILY_IPMR, + .rule_size = sizeof(struct ipmr_rule), +@@ -236,6 +316,156 @@ static const struct fib_rules_ops __net_ + .owner = THIS_MODULE, + }; + ++/* QCA ECM qca-mcs support - Start */ ++/* ipmr_register_mfc_event_offload_callback() ++ * Register the IPv4 Multicast update offload callback with IPMR ++ */ ++bool ipmr_register_mfc_event_offload_callback( ++ ipmr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ipmr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ipmr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ipmr_register_mfc_event_offload_callback); ++ ++/* ipmr_unregister_mfc_event_offload_callback() ++ * De-register the IPv4 Multicast update offload callback with IPMR ++ */ ++void ipmr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ipmr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ipmr_unregister_mfc_event_offload_callback); ++ ++/* ipmr_find_mfc_entry() ++ * Returns destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ipmr_find_mfc_entry(struct net *net, __be32 origin, __be32 group, ++ u32 max_dest_cnt, u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ spin_lock(&mrt_lock); ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ipmr_find_mfc_entry); ++ ++/* ipmr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ipmr_mfc_stats_update(struct net *net, __be32 origin, __be32 group, ++ u64 pkts_in, u64 bytes_in, ++ u64 pkts_out, u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc_cache *cache; ++ ++ mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); ++ if (!mrt) ++ return -ENOENT; ++ ++ rcu_read_lock(); ++ cache = ipmr_cache_find(mrt, origin, group); ++ if (!cache) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ spin_lock(&mrt_lock); ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ cache->_c.mfc_un.res.pkt += pkts_out; ++ cache->_c.mfc_un.res.bytes += bytes_out; ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ spin_unlock(&mrt_lock); ++ rcu_read_unlock(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ipmr_mfc_stats_update); ++/* QCA ECM qca-mcs support - End */ ++ + static int __net_init ipmr_rules_init(struct net *net) + { + struct fib_rules_ops *ops; +@@ -1191,6 +1421,10 @@ static int ipmr_mfc_delete(struct mr_tab + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); + mroute_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* QCA ECM qca-mcs support - Start */ ++ /* Inform offload modules of the delete event */ ++ ipmr_sync_entry_delete(c->mfc_origin, c->mfc_mcastgrp); ++ /* QCA ECM qca-mcs support - End */ + + return 0; + } +@@ -1221,6 +1455,10 @@ static int ipmr_mfc_add(struct net *net, + call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, + mrt->id); + mroute_netlink_event(mrt, c, RTM_NEWROUTE); ++ /* QCA ECM qca-mcs support - Start */ ++ /* Inform offload modules of the update event */ ++ ipmr_sync_entry_update(mrt, c); ++ /* QCA ECM qca-mcs support - End */ + return 0; + } + +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -74,6 +74,9 @@ static struct net_device *vif_dev_read(c + /* Special spinlock for queue of unresolved entries */ + static DEFINE_SPINLOCK(mfc_unres_lock); + ++/* Spinlock for offload */ ++static DEFINE_SPINLOCK(lock); /* QCA qca-mcs support */ ++ + /* We return to original Alan's scheme. Hash table of resolved + entries is changed only in process context and protected + with weak lock mrt_lock. Queue of unresolved entries is protected +@@ -101,6 +104,13 @@ static int ip6mr_rtm_dumproute(struct sk + struct netlink_callback *cb); + static void mroute_clean_tables(struct mr_table *mrt, int flags); + static void ipmr_expire_process(struct timer_list *t); ++/* QCA qca-mcs support - Start */ ++static struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt, ++ const struct in6_addr *origin, ++ const struct in6_addr *mcastgrp); ++static ip6mr_mfc_event_offload_callback_t __rcu ++ ip6mr_mfc_event_offload_callback; ++/* QCA qca-mcs support - End */ + + #ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES + #define ip6mr_for_each_table(mrt, net) \ +@@ -375,6 +385,84 @@ static struct mfc6_cache_cmp_arg ip6mr_m + .mf6c_mcastgrp = IN6ADDR_ANY_INIT, + }; + ++/* QCA qca-mcs support - Start */ ++/* ip6mr_sync_entry_update() ++ * Call the registered offload callback to report an update to a multicast ++ * route entry. The callback receives the list of destination interfaces and ++ * the interface count ++ */ ++static void ip6mr_sync_entry_update(struct mr_table *mrt, ++ struct mfc6_cache *cache) ++{ ++ int vifi, dest_if_count = 0; ++ u32 dest_dev[MAXMIFS]; ++ struct in6_addr mc_origin, mc_group; ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ memset(dest_dev, 0, sizeof(dest_dev)); ++ ++ spin_lock(&mrt_lock); ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ if (dest_if_count == MAXMIFS) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ ++ memcpy(&mc_origin, &cache->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &cache->mf6c_mcastgrp, sizeof(struct in6_addr)); ++ spin_unlock(&mrt_lock); ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(&mc_group, &mc_origin, dest_if_count, dest_dev, ++ IP6MR_MFC_EVENT_UPDATE); ++ rcu_read_unlock(); ++} ++ ++/* ip6mr_sync_entry_delete() ++ * Call the registered offload callback to inform of a multicast route entry ++ * delete event ++ */ ++static void ip6mr_sync_entry_delete(struct in6_addr *mc_origin, ++ struct in6_addr *mc_group) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (!offload_update_cb_f) { ++ rcu_read_unlock(); ++ return; ++ } ++ ++ offload_update_cb_f(mc_group, mc_origin, 0, NULL, ++ IP6MR_MFC_EVENT_DELETE); ++ rcu_read_unlock(); ++} ++/* QCA qca-mcs support - End */ ++ + static struct mr_table_ops ip6mr_mr_table_ops = { + .rht_params = &ip6mr_rht_params, + .cmparg_any = &ip6mr_mr_table_ops_cmparg_any, +@@ -697,6 +785,151 @@ static int call_ip6mr_mfc_entry_notifier + &mfc->_c, tb_id, &net->ipv6.ipmr_seq); + } + ++/* QCA qca-mcs support - Start */ ++/* ip6mr_register_mfc_event_offload_callback() ++ * Register the IPv6 multicast update callback for offload modules ++ */ ++bool ip6mr_register_mfc_event_offload_callback( ++ ip6mr_mfc_event_offload_callback_t mfc_offload_cb) ++{ ++ ip6mr_mfc_event_offload_callback_t offload_update_cb_f; ++ ++ rcu_read_lock(); ++ offload_update_cb_f = rcu_dereference(ip6mr_mfc_event_offload_callback); ++ ++ if (offload_update_cb_f) { ++ rcu_read_unlock(); ++ return false; ++ } ++ rcu_read_unlock(); ++ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, mfc_offload_cb); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++ return true; ++} ++EXPORT_SYMBOL(ip6mr_register_mfc_event_offload_callback); ++ ++/* ip6mr_unregister_mfc_event_offload_callback() ++ * De-register the IPv6 multicast update callback for offload modules ++ */ ++void ip6mr_unregister_mfc_event_offload_callback(void) ++{ ++ spin_lock(&lock); ++ rcu_assign_pointer(ip6mr_mfc_event_offload_callback, NULL); ++ spin_unlock(&lock); ++ synchronize_rcu(); ++} ++EXPORT_SYMBOL(ip6mr_unregister_mfc_event_offload_callback); ++ ++/* ip6mr_find_mfc_entry() ++ * Return the destination interface list for a particular multicast flow, and ++ * the number of interfaces in the list ++ */ ++int ip6mr_find_mfc_entry(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u32 max_dest_cnt, ++ u32 dest_dev[]) ++{ ++ int vifi, dest_if_count = 0; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ for (vifi = 0; vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if (!((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255))) { ++ continue; ++ } ++ ++ /* We have another valid destination interface entry. Check if ++ * the number of the destination interfaces for the route is ++ * exceeding the size of the array given to us ++ */ ++ if (dest_if_count == max_dest_cnt) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ dest_dev[dest_if_count] = mrt->vif_table[vifi].dev->ifindex; ++ dest_if_count++; ++ } ++ spin_unlock(&mrt_lock); ++ ++ return dest_if_count; ++} ++EXPORT_SYMBOL(ip6mr_find_mfc_entry); ++ ++/* ip6mr_mfc_stats_update() ++ * Update the MFC/VIF statistics for offloaded flows ++ */ ++int ip6mr_mfc_stats_update(struct net *net, struct in6_addr *origin, ++ struct in6_addr *group, u64 pkts_in, ++ u64 bytes_in, uint64_t pkts_out, ++ u64 bytes_out) ++{ ++ int vif, vifi; ++ struct mr_table *mrt; ++ struct mfc6_cache *cache; ++ ++ mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); ++ ++ if (!mrt) ++ return -ENOENT; ++ ++ spin_lock(&mrt_lock); ++ cache = ip6mr_cache_find(mrt, origin, group); ++ if (!cache) { ++ spin_unlock(&mrt_lock); ++ return -ENOENT; ++ } ++ ++ vif = cache->_c.mfc_parent; ++ ++ if (!VIF_EXISTS(mrt, vif)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ ++ mrt->vif_table[vif].pkt_in += pkts_in; ++ mrt->vif_table[vif].bytes_in += bytes_in; ++ cache->_c.mfc_un.res.pkt += pkts_out; ++ cache->_c.mfc_un.res.bytes += bytes_out; ++ ++ for (vifi = cache->_c.mfc_un.res.minvif; ++ vifi < cache->_c.mfc_un.res.maxvif; vifi++) { ++ if ((cache->_c.mfc_un.res.ttls[vifi] > 0) && ++ (cache->_c.mfc_un.res.ttls[vifi] < 255)) { ++ if (!VIF_EXISTS(mrt, vifi)) { ++ spin_unlock(&mrt_lock); ++ return -EINVAL; ++ } ++ mrt->vif_table[vifi].pkt_out += pkts_out; ++ mrt->vif_table[vifi].bytes_out += bytes_out; ++ } ++ } ++ ++ spin_unlock(&mrt_lock); ++ return 0; ++} ++EXPORT_SYMBOL(ip6mr_mfc_stats_update); ++/* QCA qca-mcs support - End */ ++ + /* Delete a VIF entry */ + static int mif6_delete(struct mr_table *mrt, int vifi, int notify, + struct list_head *head) +@@ -1221,6 +1454,7 @@ static int ip6mr_mfc_delete(struct mr_ta + int parent) + { + struct mfc6_cache *c; ++ struct in6_addr mc_origin, mc_group; /* QCA qca-mcs support */ + + /* The entries are added/deleted only under RTNL */ + rcu_read_lock(); +@@ -1229,6 +1463,11 @@ static int ip6mr_mfc_delete(struct mr_ta + rcu_read_unlock(); + if (!c) + return -ENOENT; ++ ++ /* QCA qca-mcs support - Start */ ++ memcpy(&mc_origin, &c->mf6c_origin, sizeof(struct in6_addr)); ++ memcpy(&mc_group, &c->mf6c_mcastgrp, sizeof(struct in6_addr)); ++ /* QCA qca-mcs support - End */ + rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params); + list_del_rcu(&c->_c.list); + +@@ -1236,6 +1475,11 @@ static int ip6mr_mfc_delete(struct mr_ta + FIB_EVENT_ENTRY_DEL, c, mrt->id); + mr6_netlink_event(mrt, c, RTM_DELROUTE); + mr_cache_put(&c->_c); ++ /* QCA qca-mcs support - Start */ ++ /* Inform offload modules of the delete event */ ++ ip6mr_sync_entry_delete(&mc_origin, &mc_group); ++ /* QCA qca-mcs support - End */ ++ + return 0; + } + +@@ -1457,6 +1701,10 @@ static int ip6mr_mfc_add(struct net *net + call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, + c, mrt->id); + mr6_netlink_event(mrt, c, RTM_NEWROUTE); ++ /* QCA qca-mcs support - Start */ ++ /* Inform offload modules of the update event */ ++ ip6mr_sync_entry_update(mrt, c); ++ /* QCA qca-mcs support - End */ + return 0; + } + diff --git a/target/linux/qualcommax/patches-6.6/0605-1-qca-nss-cfi-support.patch b/target/linux/qualcommax/patches-6.6/0605-1-qca-nss-cfi-support.patch new file mode 100644 index 00000000000000..a0ed16e38f242e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0605-1-qca-nss-cfi-support.patch @@ -0,0 +1,127 @@ +--- a/crypto/authenc.c ++++ b/crypto/authenc.c +@@ -417,6 +417,8 @@ static int crypto_authenc_create(struct + enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + ++ inst->alg.base.cra_flags |= (auth_base->cra_flags | ++ enc->base.cra_flags) & CRYPTO_ALG_NOSUPP_SG; + inst->alg.base.cra_priority = enc->base.cra_priority * 10 + + auth_base->cra_priority; + inst->alg.base.cra_blocksize = enc->base.cra_blocksize; +--- a/include/linux/crypto.h ++++ b/include/linux/crypto.h +@@ -101,6 +101,11 @@ + #define CRYPTO_NOLOAD 0x00008000 + + /* ++ * Set this flag if algorithm does not support SG list transforms ++ */ ++#define CRYPTO_ALG_NOSUPP_SG 0x0000c000 ++ ++/* + * The algorithm may allocate memory during request processing, i.e. during + * encryption, decryption, or hashing. Users can request an algorithm with this + * flag unset if they can't handle memory allocation failures. +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -3,6 +3,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -658,6 +658,7 @@ static int esp_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -669,6 +670,11 @@ static int esp_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -890,6 +896,7 @@ static int esp_input(struct xfrm_state * + u8 *iv; + struct scatterlist *sg; + int err = -EINVAL; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) + goto out; +@@ -897,6 +904,12 @@ static int esp_input(struct xfrm_state * + if (elen <= 0) + goto out; + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -15,6 +15,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -696,6 +696,7 @@ static int esp6_output(struct xfrm_state + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; ++ bool nosupp_sg; + + esp.inplace = true; + +@@ -707,6 +708,11 @@ static int esp6_output(struct xfrm_state + aead = x->data; + alen = crypto_aead_authsize(aead); + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ return -ENOMEM; ++ } ++ + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); +@@ -934,6 +940,7 @@ static int esp6_input(struct xfrm_state + __be32 *seqhi; + u8 *iv; + struct scatterlist *sg; ++ bool nosupp_sg; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + ivlen)) { + ret = -EINVAL; +@@ -945,6 +952,12 @@ static int esp6_input(struct xfrm_state + goto out; + } + ++ nosupp_sg = crypto_tfm_alg_type(&aead->base) & CRYPTO_ALG_NOSUPP_SG; ++ if (nosupp_sg && skb_linearize(skb)) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + diff --git a/target/linux/qualcommax/patches-6.6/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch b/target/linux/qualcommax/patches-6.6/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch new file mode 100644 index 00000000000000..25f99fc4adef3c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0611-ipv6-Fix-null-pointer-dereference-in-ipv6-output.patch @@ -0,0 +1,80 @@ +From eee3a7956b943dd3e23a74fbb5bfe89405eb0782 Mon Sep 17 00:00:00 2001 +From: Andrea Righi +Date: Mon, 6 Dec 2021 17:34:47 +0100 +Subject: UBUNTU: SAUCE: ipv6: fix NULL pointer dereference in ip6_output() + +It is possible to trigger a NULL pointer dereference by running the srv6 +net kselftest (tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh): + +[ 249.051216] BUG: kernel NULL pointer dereference, address: 0000000000000378 +[ 249.052331] #PF: supervisor read access in kernel mode +[ 249.053137] #PF: error_code(0x0000) - not-present page +[ 249.053960] PGD 0 P4D 0 +[ 249.054376] Oops: 0000 [#1] PREEMPT SMP NOPTI +[ 249.055083] CPU: 1 PID: 21 Comm: ksoftirqd/1 Tainted: G E 5.16.0-rc4 #2 +[ 249.056328] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 +[ 249.057632] RIP: 0010:ip6_forward+0x53c/0xab0 +[ 249.058354] Code: 49 c7 44 24 20 00 00 00 00 48 83 e0 fe 48 8b 40 30 48 3d 70 b2 b5 81 0f 85 b5 04 00 00 e8 7c f2 ff ff 41 89 c5 e9 17 01 00 00 <44> 8b 93 78 03 00 00 45 85 d2 0f 85 92 fb ff ff 49 8b 54 24 10 48 +[ 249.061274] RSP: 0018:ffffc900000cbb30 EFLAGS: 00010246 +[ 249.062042] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff8881051d3400 +[ 249.063141] RDX: ffff888104bda000 RSI: 00000000000002c0 RDI: 0000000000000000 +[ 249.064264] RBP: ffffc900000cbbc8 R08: 0000000000000000 R09: 0000000000000000 +[ 249.065376] R10: 0000000000000040 R11: 0000000000000000 R12: ffff888103409800 +[ 249.066498] R13: ffff8881051d3410 R14: ffff888102725280 R15: ffff888103525000 +[ 249.067619] FS: 0000000000000000(0000) GS:ffff88813bc80000(0000) knlGS:0000000000000000 +[ 249.068881] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 249.069777] CR2: 0000000000000378 CR3: 0000000104980000 CR4: 0000000000750ee0 +[ 249.070907] PKRU: 55555554 +[ 249.071337] Call Trace: +[ 249.071730] +[ 249.072070] ? debug_smp_processor_id+0x17/0x20 +[ 249.072807] seg6_input_core+0x2bb/0x2d0 +[ 249.073436] ? _raw_spin_unlock_irqrestore+0x29/0x40 +[ 249.074225] seg6_input+0x3b/0x130 +[ 249.074768] lwtunnel_input+0x5e/0xa0 +[ 249.075357] ip_rcv+0x17b/0x190 +[ 249.075867] ? update_load_avg+0x82/0x600 +[ 249.076514] __netif_receive_skb_one_core+0x86/0xa0 +[ 249.077231] __netif_receive_skb+0x15/0x60 +[ 249.077843] process_backlog+0x97/0x160 +[ 249.078389] __napi_poll+0x31/0x170 +[ 249.078912] net_rx_action+0x229/0x270 +[ 249.079506] __do_softirq+0xef/0x2ed +[ 249.080085] run_ksoftirqd+0x37/0x50 +[ 249.080663] smpboot_thread_fn+0x193/0x230 +[ 249.081312] kthread+0x17a/0x1a0 +[ 249.081847] ? smpboot_register_percpu_thread+0xe0/0xe0 +[ 249.082677] ? set_kthread_struct+0x50/0x50 +[ 249.083340] ret_from_fork+0x22/0x30 +[ 249.083926] +[ 249.090295] ---[ end trace 1998d7ba5965a365 ]--- + +It looks like commit 0857d6f8c759 ("ipv6: When forwarding count rx stats +on the orig netdev") tries to determine the right netdev to account the +rx stats, but in this particular case it's failing and the netdev is +NULL. + +Fallback to the previous method of determining the netdev interface (via +skb->dev) to account the rx stats when the orig netdev can't be +determined. + +Fixes: 0857d6f8c759 ("ipv6: When forwarding count rx stats on the orig netdev") +Signed-off-by: Andrea Righi +(cherry picked from https://lore.kernel.org/lkml/20211206163447.991402-1-andrea.righi@canonical.com/T/#u) +Signed-off-by: Andrea Righi +--- + net/ipv6/ip6_output.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/ipv6/ip6_output.c ++++ b/net/ipv6/ip6_output.c +@@ -498,6 +498,9 @@ int ip6_forward(struct sk_buff *skb) + u32 mtu; + + idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); ++ if (unlikely(!idev)) ++ idev = __in6_dev_get_safely(skb->dev); ++ + if (net->ipv6.devconf_all->forwarding == 0) + goto error; + diff --git a/target/linux/qualcommax/patches-6.6/9990-1-qca-skb_recycler-support.patch b/target/linux/qualcommax/patches-6.6/9990-1-qca-skb_recycler-support.patch new file mode 100644 index 00000000000000..e59b5e5398aea4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/9990-1-qca-skb_recycler-support.patch @@ -0,0 +1,384 @@ +--- a/include/linux/cpuhotplug.h ++++ b/include/linux/cpuhotplug.h +@@ -94,6 +94,7 @@ enum cpuhp_state { + CPUHP_RADIX_DEAD, + CPUHP_PAGE_ALLOC, + CPUHP_NET_DEV_DEAD, ++ CPUHP_SKB_RECYCLER_DEAD, + CPUHP_PCI_XGENE_DEAD, + CPUHP_IOMMU_IOVA_DEAD, + CPUHP_LUSTRE_CFS_DEAD, +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -1065,6 +1065,10 @@ struct sk_buff { + /* only useable after checking ->active_extensions != 0 */ + struct skb_ext *extensions; + #endif ++ ++#ifdef CONFIG_DEBUG_OBJECTS_SKBUFF ++ void *free_addr; ++#endif + }; + + /* if you move pkt_type around you also must adapt those constants */ +@@ -1250,7 +1254,7 @@ static inline void kfree_skb_list(struct sk_buff *segs) + kfree_skb_list_reason(segs, SKB_DROP_REASON_NOT_SPECIFIED); + } + +-#ifdef CONFIG_TRACEPOINTS ++#ifdef CONFIG_SKB_RECYCLER + void consume_skb(struct sk_buff *skb); + #else + static inline void consume_skb(struct sk_buff *skb) +@@ -1262,6 +1266,9 @@ static inline void consume_skb(struct sk_buff *skb) + void __consume_stateless_skb(struct sk_buff *skb); + void __kfree_skb(struct sk_buff *skb); + extern struct kmem_cache *skbuff_cache; ++extern void kfree_skbmem(struct sk_buff *skb); ++extern void skb_release_data(struct sk_buff *skb, enum skb_drop_reason reason, ++ bool napi_safe); + + void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); + bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -369,6 +369,27 @@ config NET_FLOW_LIMIT + with many clients some protection against DoS by a single (spoofed) + flow that greatly exceeds average workload. + ++config SKB_RECYCLER ++ bool "Generic skb recycling" ++ default y ++ help ++ SKB_RECYCLER is used to implement RX-to-RX skb recycling. ++ This config enables the recycling scheme for bridging and ++ routing workloads. It can reduce skbuff freeing or ++ reallocation overhead. ++ ++config SKB_RECYCLER_MULTI_CPU ++ bool "Cross-CPU recycling for CPU-locked workloads" ++ depends on SMP && SKB_RECYCLER ++ default n ++ ++config ALLOC_SKB_PAGE_FRAG_DISABLE ++ bool "Disable page fragment based skbuff payload allocations" ++ depends on !SKB_RECYCLER ++ default n ++ help ++ Disable page fragment based allocations for skbuff payloads. ++ + menu "Network testing" + + config NET_PKTGEN +--- a/net/core/Makefile ++++ b/net/core/Makefile +@@ -41,3 +41,4 @@ obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o + obj-$(CONFIG_BPF_SYSCALL) += sock_map.o + obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o + obj-$(CONFIG_OF) += of_net.o ++obj-$(CONFIG_SKB_RECYCLER) += skbuff_recycle.o +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -6016,10 +6016,16 @@ static int process_backlog(struct napi_struct *napi, int quota) + + napi->weight = READ_ONCE(dev_rx_weight); + while (again) { +- struct sk_buff *skb; ++ struct sk_buff *skb, *next_skb; + + while ((skb = __skb_dequeue(&sd->process_queue))) { + rcu_read_lock(); ++ ++ next_skb = skb_peek(&sd->process_queue); ++ if (likely(next_skb)) { ++ prefetch(next_skb->data); ++ } ++ + __netif_receive_skb(skb); + rcu_read_unlock(); + input_queue_head_incr(sd); +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -87,6 +87,31 @@ + + #include "dev.h" + #include "sock_destructor.h" ++#include "skbuff_recycle.h" ++ ++struct kmem_cache *skb_data_cache; ++/* ++ * For low memory profile, NSS_SKB_FIXED_SIZE_2K is enabled and ++ * CONFIG_SKB_RECYCLER is disabled. For premium and enterprise profile ++ * CONFIG_SKB_RECYCLER is enabled and NSS_SKB_FIXED_SIZE_2K is disabled. ++ * Irrespective of NSS_SKB_FIXED_SIZE_2K enabled/disabled, the ++ * CONFIG_SKB_RECYCLER and __LP64__ determines the value of SKB_DATA_CACHE_SIZE ++ */ ++#if defined(CONFIG_SKB_RECYCLER) ++/* ++ * 2688 for 64bit arch, 2624 for 32bit arch ++ */ ++#define SKB_DATA_CACHE_SIZE (SKB_DATA_ALIGN(SKB_RECYCLE_SIZE + NET_SKB_PAD) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#else ++/* ++ * 2368 for 64bit arch, 2176 for 32bit arch ++ */ ++#if defined(__LP64__) ++#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1984 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#else ++#define SKB_DATA_CACHE_SIZE ((SKB_DATA_ALIGN(1856 + NET_SKB_PAD)) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#endif ++#endif + + struct kmem_cache *skbuff_cache __ro_after_init; + static struct kmem_cache *skbuff_fclone_cache __ro_after_init; +@@ -551,21 +576,20 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, + bool *pfmemalloc) + { + bool ret_pfmemalloc = false; +- size_t obj_size; ++ unsigned int obj_size = *size; + void *obj; + + obj_size = SKB_HEAD_ALIGN(*size); +- if (obj_size <= SKB_SMALL_HEAD_CACHE_SIZE && +- !(flags & KMALLOC_NOT_NORMAL_BITS)) { +- obj = kmem_cache_alloc_node(skb_small_head_cache, +- flags | __GFP_NOMEMALLOC | __GFP_NOWARN, +- node); +- *size = SKB_SMALL_HEAD_CACHE_SIZE; ++ if (obj_size > SZ_2K && obj_size <= SKB_DATA_CACHE_SIZE) { ++ obj = kmem_cache_alloc_node(skb_data_cache, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, ++ node); ++ *size = SKB_DATA_CACHE_SIZE; + if (obj || !(gfp_pfmemalloc_allowed(flags))) + goto out; + /* Try again but now we are using pfmemalloc reserves */ + ret_pfmemalloc = true; +- obj = kmem_cache_alloc_node(skb_small_head_cache, flags, node); ++ obj = kmem_cache_alloc_node(skb_data_cache, flags, node); + goto out; + } + +@@ -648,10 +671,12 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, + * aligned memory blocks, unless SLUB/SLAB debug is enabled. + * Both skb->head and skb_shared_info are cache line aligned. + */ ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, node, &pfmemalloc); + if (unlikely(!data)) + goto nodata; +- /* kmalloc_size_roundup() might give us more room than requested. ++ /* kmalloc_reserve(size) might give us more room than requested. + * Put skb_shared_info exactly at the end of allocated zone, + * to allow max possible filling before reallocation. + */ +@@ -686,7 +711,7 @@ EXPORT_SYMBOL(__alloc_skb); + /** + * __netdev_alloc_skb - allocate an skbuff for rx on a specific device + * @dev: network device to receive on +- * @len: length to allocate ++ * @length: length to allocate + * @gfp_mask: get_free_pages mask, passed to alloc_skb + * + * Allocate a new &sk_buff and assign it a usage count of one. The +@@ -696,29 +721,53 @@ EXPORT_SYMBOL(__alloc_skb); + * + * %NULL is returned if there is no free memory. + */ +-struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, +- gfp_t gfp_mask) ++struct sk_buff *__netdev_alloc_skb(struct net_device *dev, ++ unsigned int length, gfp_t gfp_mask) + { +- struct page_frag_cache *nc; + struct sk_buff *skb; ++ unsigned int len = length; ++ ++#ifdef CONFIG_SKB_RECYCLER ++ skb = skb_recycler_alloc(dev, length); ++ if (likely(skb)) ++ return skb; ++ ++ len = SKB_RECYCLE_SIZE; ++ if (unlikely(length > SKB_RECYCLE_SIZE)) ++ len = length; ++ ++ skb = __alloc_skb(len + NET_SKB_PAD, gfp_mask, ++ SKB_ALLOC_RX, NUMA_NO_NODE); ++ if (!skb) ++ goto skb_fail; ++ goto skb_success; ++#else ++ struct page_frag_cache *nc; + bool pfmemalloc; ++ bool page_frag_alloc_enable = true; + void *data; + + len += NET_SKB_PAD; + ++ ++#ifdef CONFIG_ALLOC_SKB_PAGE_FRAG_DISABLE ++ page_frag_alloc_enable = false; ++#endif + /* If requested length is either too small or too big, + * we use kmalloc() for skb->head allocation. + */ + if (len <= SKB_WITH_OVERHEAD(1024) || + len > SKB_WITH_OVERHEAD(PAGE_SIZE) || +- (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) { ++ (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA)) || ++ !page_frag_alloc_enable) { + skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE); + if (!skb) + goto skb_fail; + goto skb_success; + } + +- len = SKB_HEAD_ALIGN(len); ++ len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ len = SKB_DATA_ALIGN(len); + + if (sk_memalloc_socks()) + gfp_mask |= __GFP_MEMALLOC; +@@ -747,6 +796,7 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len, + if (pfmemalloc) + skb->pfmemalloc = 1; + skb->head_frag = 1; ++#endif + + skb_success: + skb_reserve(skb, NET_SKB_PAD); +@@ -817,7 +867,8 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len, + data = page_frag_alloc_1k(&nc->page_small, gfp_mask); + pfmemalloc = NAPI_SMALL_PAGE_PFMEMALLOC(nc->page_small); + } else { +- len = SKB_HEAD_ALIGN(len); ++ len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ len = SKB_DATA_ALIGN(len); + + data = page_frag_alloc(&nc->page, len, gfp_mask); + pfmemalloc = nc->page.pfmemalloc; +@@ -975,7 +1026,7 @@ static void skb_free_head(struct sk_buff *skb, bool napi_safe) + } + } + +-static void skb_release_data(struct sk_buff *skb, enum skb_drop_reason reason, ++void skb_release_data(struct sk_buff *skb, enum skb_drop_reason reason, + bool napi_safe) + { + struct skb_shared_info *shinfo = skb_shinfo(skb); +@@ -1018,7 +1069,7 @@ static void skb_release_data(struct sk_buff *skb, enum skb_drop_reason reason, + /* + * Free an skbuff by memory without cleaning the state. + */ +-static void kfree_skbmem(struct sk_buff *skb) ++void kfree_skbmem(struct sk_buff *skb) + { + struct sk_buff_fclones *fclones; + +@@ -1282,7 +1333,6 @@ void skb_tx_error(struct sk_buff *skb) + } + EXPORT_SYMBOL(skb_tx_error); + +-#ifdef CONFIG_TRACEPOINTS + /** + * consume_skb - free an skbuff + * @skb: buffer to free +@@ -1291,13 +1341,48 @@ EXPORT_SYMBOL(skb_tx_error); + * Functions identically to kfree_skb, but kfree_skb assumes that the frame + * is being dropped after a failure and notes that + */ ++#ifdef CONFIG_SKB_RECYCLER + void consume_skb(struct sk_buff *skb) + { + if (!skb_unref(skb)) + return; ++ prefetch(&skb->destructor); ++ ++ /*Tian: Not sure if we need to continue using this since ++ * since unref does the work in 5.4 ++ */ ++ ++ /* ++ if (likely(atomic_read(&skb->users) == 1)) ++ smp_rmb(); ++ else if (likely(!atomic_dec_and_test(&skb->users))) ++ return; ++ */ + ++ /* If possible we'd like to recycle any skb rather than just free it, ++ * but in order to do that we need to release any head state too. ++ * We don't want to do this later because we'll be in a pre-emption ++ * disabled state. ++ */ ++ skb_release_head_state(skb); ++ ++ /* Can we recycle this skb? If we can then it will be much faster ++ * for us to recycle this one later than to allocate a new one ++ * from scratch. ++ */ ++ if (likely(skb->head) && likely(skb_recycler_consume(skb))) ++ return; ++ ++#ifdef CONFIG_TRACEPOINTS + trace_consume_skb(skb, __builtin_return_address(0)); +- __kfree_skb(skb); ++#endif ++ /* We're not recycling so now we need to do the rest of what we would ++ * have done in __kfree_skb (above and beyond the skb_release_head_state ++ * that we already did). ++ */ ++ if (likely(skb->head)) ++ skb_release_data(skb, SKB_CONSUMED, false); ++ kfree_skbmem(skb); + } + EXPORT_SYMBOL(consume_skb); + #endif +@@ -2107,6 +2192,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + goto nodata; +@@ -4854,6 +4941,10 @@ static void skb_extensions_init(void) {} + + void __init skb_init(void) + { ++ skb_data_cache = kmem_cache_create_usercopy("skb_data_cache", ++ SKB_DATA_CACHE_SIZE, ++ 0, SLAB_PANIC, 0, SKB_DATA_CACHE_SIZE, ++ NULL); + skbuff_cache = kmem_cache_create_usercopy("skbuff_head_cache", + sizeof(struct sk_buff), + 0, +@@ -4879,6 +4970,7 @@ void __init skb_init(void) + SKB_SMALL_HEAD_HEADROOM, + NULL); + skb_extensions_init(); ++ skb_recycler_init(); + } + + static int +@@ -6382,6 +6474,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + return -ENOMEM; +@@ -6498,6 +6592,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, + if (skb_pfmemalloc(skb)) + gfp_mask |= __GFP_MEMALLOC; + ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + data = kmalloc_reserve(&size, gfp_mask, NUMA_NO_NODE, NULL); + if (!data) + return -ENOMEM; diff --git a/target/linux/qualcommax/patches-6.6/9991-arm64-dts-qcom-disable-swiotlb-for-64mb-savings.patch b/target/linux/qualcommax/patches-6.6/9991-arm64-dts-qcom-disable-swiotlb-for-64mb-savings.patch new file mode 100644 index 00000000000000..bb610079aad154 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/9991-arm64-dts-qcom-disable-swiotlb-for-64mb-savings.patch @@ -0,0 +1,99 @@ +--- a/arch/arm64/boot/dts/qcom/ipq6010-wax214.dts ++++ b/arch/arm64/boot/dts/qcom/ipq6010-wax214.dts +@@ -25,7 +25,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8070-cax1800.dts +@@ -27,7 +27,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8070-rm2-6.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8070-rm2-6.dts +@@ -32,7 +32,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8071-ax3600.dtsi +@@ -20,7 +20,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_0"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_0"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8071-mf269.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8071-mf269.dts +@@ -24,7 +24,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_0"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_0"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8072-ax880.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8072-ax880.dts +@@ -29,7 +29,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8072-wax218.dts +@@ -27,7 +27,7 @@ + * Netgear's U-Boot adds "ubi.mtd=rootfs root=mtd:ubi_rootfs" + * That fails to create a UBI block device, so add it here. + */ +- bootargs-append = " ubi.block=0,rootfs root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce ubi.block=0,rootfs root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts ++++ b/arch/arm64/boot/dts/qcom/ipq8072-wpq873.dts +@@ -31,7 +31,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_1"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_1"; + }; + + keys { +--- a/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi +@@ -29,7 +29,7 @@ + + chosen { + stdout-path = "serial0:115200n8"; +- bootargs-append = " root=/dev/ubiblock0_0"; ++ bootargs-append = " coherent_pool=2M swiotlb=noforce root=/dev/ubiblock0_0"; + }; + + keys { diff --git a/target/linux/qualcommax/patches-6.6/9999-add-nss-macsec.patch b/target/linux/qualcommax/patches-6.6/9999-add-nss-macsec.patch new file mode 100644 index 00000000000000..1be086be21789f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/9999-add-nss-macsec.patch @@ -0,0 +1,13 @@ +--- a/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi 2024-01-08 16:04:51.957322224 -0500 ++++ b/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi 2024-01-08 16:09:29.079630738 -0500 +@@ -268,4 +268,10 @@ + }; + }; + }; ++ nss-macsec1 { ++ compatible = "qcom,nss-macsec"; ++ phy_addr = <0x1c>; ++ phy_access_mode = <0x00>; ++ mdiobus = <&mdio>; ++ }; + }; diff --git a/target/linux/qualcommax/patches-6.6/9999-revert-crypto-api-disallow-identical-driver-names.patch b/target/linux/qualcommax/patches-6.6/9999-revert-crypto-api-disallow-identical-driver-names.patch new file mode 100644 index 00000000000000..3f7f58dfee5af2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/9999-revert-crypto-api-disallow-identical-driver-names.patch @@ -0,0 +1,10 @@ +--- a/crypto/algapi.c ++++ b/crypto/algapi.c +@@ -341,7 +341,6 @@ __crypto_register_alg(struct crypto_alg + } + + if (!strcmp(q->cra_driver_name, alg->cra_name) || +- !strcmp(q->cra_driver_name, alg->cra_driver_name) || + !strcmp(q->cra_name, alg->cra_driver_name)) + goto err; + } diff --git a/target/linux/qualcommax/patches-6.6/9999-silence-UBI-NAND-warnings.patch b/target/linux/qualcommax/patches-6.6/9999-silence-UBI-NAND-warnings.patch new file mode 100644 index 00000000000000..646e683dc8df70 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/9999-silence-UBI-NAND-warnings.patch @@ -0,0 +1,13 @@ +--- a/drivers/mtd/mtdblock.c ++++ b/drivers/mtd/mtdblock.c +@@ -261,10 +261,6 @@ static int mtdblock_open(struct mtd_blkt + return 0; + } + +- if (mtd_type_is_nand(mbd->mtd)) +- pr_warn_ratelimited("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", +- mbd->tr->name, mbd->mtd->name); +- + /* OK, it's not open. Create cache info for it */ + mtdblk->count = 1; + mutex_init(&mtdblk->cache_mutex);