diff --git a/Config.in b/Config.in index 6d71d4c6e6f291..3210af928c69b2 100644 --- a/Config.in +++ b/Config.in @@ -38,3 +38,5 @@ source "target/sdk/Config.in" source "target/toolchain/Config.in" source "tmp/.config-package.in" + +source "config/Config-ipq.in" diff --git a/README.md b/README.md index 9141b2392df73e..b0680d77490a62 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,168 @@ -![OpenWrt logo](include/logo.png) +# NSS Fork for IPQ807x +| Branch | mac80211 Version | Notes | +|-----------------------------------------------------------------------------------------|------------------|----------------------------------------------------------------------| +| [qualcommax-6.x-nss-wifi](https://github.com/qosmio/openwrt-ipq/tree/qualcommax-6.x-nss-wifi) |6.11|Current with upstream| +| [qualcommax-6.x-nss-mx4300](https://github.com/qosmio/openwrt-ipq/tree/qualcommax-6.x-nss-mx4300) |6.11|Current with upstream. [Supports Linksys MX4300](https://github.com/openwrt/openwrt/pull/16070) | +| [qualcommax-6.x-nss-wifi-6.9](https://github.com/qosmio/openwrt-ipq/tree/qualcommax-6.x-nss-wifi-6.9) |6.9.9|Current with upstream, except older mac80211| +| [qualcommax-6.x-nss-mx4300-6.9](https://github.com/qosmio/openwrt-ipq/tree/qualcommax-6.x-nss-mx4300-6.11) |6.9.9|Current with upstream, except older mac80211. [Supports Linksys MX4300](https://github.com/openwrt/openwrt/pull/16070) | -OpenWrt Project is a Linux operating system targeting embedded devices. Instead -of trying to create a single, static firmware, OpenWrt provides a fully -writable filesystem with package management. This frees you from the -application selection and configuration provided by the vendor and allows you -to customize the device through the use of packages to suit any application. -For developers, OpenWrt is the framework to build an application without having -to build a complete firmware around it; for users this means the ability for -full customization, to use the device in ways never envisioned. +NOTE: The 6.9 branches are meant for folks having issues with the current 6.11.2 version of backports/mac80211. These are mostly just meant as a workaround for the time being. I don't plan on supporting these branches longterm. -Sunshine! +The MX4300 branches will soon be merged in once the PR for MX4300 is approved upstream. -## Download +## Table of Contents +- [Overview](#overview) +- [What's NSS?](#whats-nss) +- [How Does OpenWrt "Offload" Traffic?](#how-does-openwrt-offload-traffic) +- [How Is NSS Different from OpenWrt's Offloading Options?](#how-is-nss-different-from-openwrts-offloading-options) +- [Do I Need NSS?](#do-i-need-nss) +- [OK, I Want NSS. Does My Device Support It?](#ok-i-want-nss-does-my-device-support-it) +- [Quickstart](#quickstart) +- [Important Note](#important-note) +- [Donate](#donate) -Built firmware images are available for many architectures and come with a -package selection to be used as WiFi home router. To quickly find a factory -image usable to migrate from a vendor stock firmware to OpenWrt, try the -*Firmware Selector*. +--- -* [OpenWrt Firmware Selector](https://firmware-selector.openwrt.org/) +## Overview +This repository contains a fork of OpenWrt that integrates Qualcomm's NSS (Network Subsystem) support for the IPQ807x and IPQ6018 series of SoCs. The goal of this project is to provide enhanced network performance and reliability through hardware offloading, leveraging NSS's capabilities to improve throughput and reduce CPU load. -If your device is supported, please follow the **Info** link to see install -instructions or consult the support resources listed below. +--- +#### What's NSS? -## +NSS (**N**etwork **S**ub**s**ystem) is a specialized hardware offloading engine developed by Qualcomm, integrated into their IPQ series SoCs (System-on-Chip), such as the IPQ807x and IPQ6018. NSS is designed to handle high-throughput network tasks like NAT, routing, and even security tasks such as IPsec, without burdening the main CPU cores. -An advanced user may require additional or specific package. (Toolchain, SDK, ...) For everything else than simple firmware download, try the wiki download page: +--- +#### How Does OpenWrt "Offload" Traffic? -* [OpenWrt Wiki Download](https://openwrt.org/downloads) +OpenWrt offers three primary methods for offloading network traffic, each aimed at reducing CPU load and improving throughput: -## Development +1. **Packet Steering**: Distributes network processing across multiple CPU cores. It helps balance the load on multi-core CPUs but still relies on the CPU to handle packet processing. -To build your own firmware you need a GNU/Linux, BSD or macOS system (case -sensitive filesystem required). Cygwin is unsupported because of the lack of a -case sensitive file system. +2. **Software Flow Offloading**: Accelerates traffic processing by using the CPU’s fast path, allowing more efficient handling of routing and NAT (Network Address Translation) by bypassing the kernel's normal slow path. This relies entirely on the CPU to speed up packet forwarding. -### Requirements +3. **Hardware Flow Offloading**: Available only on select devices (e.g., Mediatek), this method offloads packet forwarding directly to the network hardware using kernel-based mechanisms (nftables/iptables) to accelerate traffic. However, this is limited to devices that have hardware acceleration capabilities supported by OpenWrt. -You need the following tools to compile OpenWrt, the package names vary between -distributions. A complete list with distribution specific packages is found in -the [Build System Setup](https://openwrt.org/docs/guide-developer/build-system/install-buildsystem) -documentation. +--- +#### Why Isn't NSS Supported in Vanilla OpenWrt? -``` -binutils bzip2 diff find flex gawk gcc-6+ getopt grep install libc-dev libz-dev -make4.1+ perl python3.7+ rsync subversion unzip which -``` +NSS requires proprietary binaries (NSS firmware) and extensive patches to the Linux kernel and networking stack. Qualcomm does not openly release the necessary firmware or detailed documentation on how to integrate NSS support, making it extremely difficult for the OpenWrt community to maintain. The required patches are invasive and complex, altering significant portions of the network stack, which makes upstream integration into OpenWrt unlikely. Maintaining compatibility with each new kernel version is another barrier, as Qualcomm’s support for these patches is minimal and sporadic. -### Quickstart +--- -1. Run `./scripts/feeds update -a` to obtain all the latest package definitions - defined in feeds.conf / feeds.conf.default +#### How Is NSS Different from OpenWrt's Offloading Options? -2. Run `./scripts/feeds install -a` to install symlinks for all obtained - packages into package/feeds/ +The main difference between NSS and OpenWrt's offloading methods is that NSS provides **hardware acceleration** directly within the SoC, bypassing the CPU almost entirely for certain network tasks. OpenWrt's offloading, on the other hand, relies heavily on the **CPU** to manage and accelerate traffic, either via multi-core CPU distribution (Packet Steering) or kernel-level acceleration (Software/Hardware Flow Offloading). -3. Run `make menuconfig` to select your preferred configuration for the - toolchain, target system & firmware packages. +NSS doesn’t play nice with OpenWrt’s built-in offloading because they conflict in how they handle packets, leading to performance issues or even outright failures. -4. Run `make` to build your firmware. This will download all sources, build the - cross-compile toolchain and then cross-compile the GNU/Linux kernel & all chosen - applications for your target system. +--- -### Related Repositories +#### Key Differences: -The main repository uses multiple sub-repositories to manage packages of -different categories. All packages are installed via the OpenWrt package -manager called `opkg`. If you're looking to develop the web interface or port -packages to OpenWrt, please find the fitting repository below. +- **Packet Steering**: While it redistributes packet processing across multiple CPU cores, it still involves the CPU heavily. With NSS, dedicated hardware cores handle packet processing, so packet steering can interfere by unnecessarily involving the CPU, reducing the benefits of offloading to hardware. -* [LuCI Web Interface](https://github.com/openwrt/luci): Modern and modular - interface to control the device via a web browser. +- **Software Flow Offloading**: This uses kernel-level mechanisms (nftables/iptables) to accelerate packet forwarding by utilizing the CPU’s fast path. NSS, however, bypasses the kernel’s networking stack and offloads these tasks directly to the hardware. If both are enabled, packet handling may be done redundantly in software and hardware, leading to inefficiencies or conflicts. -* [OpenWrt Packages](https://github.com/openwrt/packages): Community repository - of ported packages. +- **Hardware Flow Offloading**: Available only for certain devices like Mediatek, this method offloads packet processing to specific hardware via kernel drivers. However, this hardware-based acceleration is entirely separate from NSS and not as efficient on Qualcomm devices. Using it alongside NSS can lead to unpredictable behavior, as both try to accelerate traffic but in incompatible ways. -* [OpenWrt Routing](https://github.com/openwrt/routing): Packages specifically - focused on (mesh) routing. +--- -* [OpenWrt Video](https://github.com/openwrt/video): Packages specifically - focused on display servers and clients (Xorg and Wayland). +#### Do I Need NSS? -## Support Information +Here are some reasons you might need NSS: -For a list of supported devices see the [OpenWrt Hardware Database](https://openwrt.org/supported_devices) +- You require high network throughput (e.g., gigabit speeds) on devices like the IPQ807x or IPQ6018 series. +- Your router handles resource-intensive tasks like NAT, VPN (IPsec), or other routing-heavy activities that would otherwise overwhelm the CPU. +- You’re seeking better performance than what OpenWrt’s software and hardware offloading options can provide. -### Documentation +However, it’s important to note that **NSS is NOT supported upstream** in OpenWrt. As of now, there are only a few community-driven projects that maintain NSS patches. -* [Quick Start Guide](https://openwrt.org/docs/guide-quick-start/start) -* [User Guide](https://openwrt.org/docs/guide-user/start) -* [Developer Documentation](https://openwrt.org/docs/guide-developer/start) -* [Technical Reference](https://openwrt.org/docs/techref/start) +I personally maintain an NSS fork of OpenWrt [qosmio/openwrt-ipq](https://github.com/qosmio/openwrt-ipq) and the necessary NSS packages [qosmio/nss-packages](https://github.com/qosmio/nss-packages). -### Support Community +--- -* [Forum](https://forum.openwrt.org): For usage, projects, discussions and hardware advise. -* [Support Chat](https://webchat.oftc.net/#openwrt): Channel `#openwrt` on **oftc.net**. +#### OK, I Want NSS. Does My Device Support It? -### Developer Community +NSS is available for most Qualcomm IPQ807x and IPQ6018 devices. If your device is part of this chipset family and supported in OpenWrt, it can run NSS. -* [Bug Reports](https://bugs.openwrt.org): Report bugs in OpenWrt -* [Dev Mailing List](https://lists.openwrt.org/mailman/listinfo/openwrt-devel): Send patches -* [Dev Chat](https://webchat.oftc.net/#openwrt-devel): Channel `#openwrt-devel` on **oftc.net**. +Supported devices include, but are not limited to: +- Devices based on the **IPQ807x** (e.g., some high-end Netgear and TP-Link routers) +- Devices based on the **IPQ6018** (e.g., certain enterprise routers) -## License +--- -OpenWrt is licensed under GPL-2.0 +## Quickstart + +1. Clone this repository: + ```bash + git clone https://github.com/qosmio/openwrt-ipq -b qualcommax-6.x-nss-wifi + cd openwrt-ipq + ``` +2. Update feeds: + ```bash + ./scripts/feeds update + ./scripts/feeds install -a + ``` +3. Copy over the seed file + ```bash + cp nss-setup/config-nss.seed .config + ``` +4. Open the `.config` in a text editor, find your device, and remove the "#" and change `"is not set"` to `"=y"` + + Example: + ```diff + -# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_dynalink_dl-wrx36 is not set + +CONFIG_TARGET_qualcommax_ipq807x_DEVICE_dynalink_dl-wrx36=y + ``` +6. Generate the full config + ```bash + make defconfig V=s + ``` +7. Now run full build + ```bash + make download -j$(nproc) V=s + make -j$(nproc) V=s + ``` +--- +### Important Note: + +Many users report issues after enabling Packet Steering or Flow Offloading (Software or Hardware), often because they are used to these options or they get carried over during a sysupgrade. Even if the setup seems to work initially, it is not optimized for NSS offloading, and you are losing the full benefits of hardware acceleration. + +If you plan to use NSS, **start fresh** and **disable all other offloading options**. + +By default OpenWrt's offloading is disabled, but if you ever happen to enable it accidentally, make sure you disable it. + +--- + ```bash + uci set network.@device[0].packet_steering=0 + uci set network.@device[0].flow_offloading=0 + uci set network.@device[0].flow_offloading_hw=0 + uci commit network + ``` +If you have questions or issues, please join the discussion on OpenWrt's forums. +[Qualcomm NSS Build](https://forum.openwrt.org/t/qualcommax-nss-build) + +## Please remember when posting about an issue: + 1. Include your device make and model. + 2. Relevant logs and screenshots. + 3. State clearly and concisely the issue you're having. + > "My router doesn't work", "I'm getting an error" + + Is not something I can help with. + 4. Include the specific commit you're building from. + > "I'm building from latest" + + Also not helpful as I'm always pushing changes... + + 5. Be respectful and mindful. I dedicate my free time to maintain and improve this project, and I do it for the benefit of the community. Remember that I'm not a full-time developer or support team—I'm just an individual sharing my work. Constructive feedback is always welcome, but please refrain from being overly critical or demanding. + +## Support the Project + +I never really thought about setting up donations before, but with so many people being receptive and appreciative and asking how to contribute, I figured, why not? Of course, this project also builds on the incredible work done by the talented devs upstream who put in countless hours into OpenWrt itself. I’ll definitely continue working on this, but if you’d like to support, every bit helps. + +[![Donate with PayPal](./paypal.png)](https://www.paypal.com/donate?business=3V3H2SZFY7DNQ&item_name=Maintaining+NSS+fork+of+OpenWRT+and+NSS+packages.) + + Cashapp + + +Consider donating to the [OpenWrt Foundation](https://openwrt.org/donate) diff --git a/cashapp.png b/cashapp.png new file mode 100644 index 00000000000000..ad2881916cb853 Binary files /dev/null and b/cashapp.png differ diff --git a/config/Config-ipq.in b/config/Config-ipq.in new file mode 100644 index 00000000000000..7b36443b1c8c5a --- /dev/null +++ b/config/Config-ipq.in @@ -0,0 +1,150 @@ +# vim: set ft=kconfig +menu "Qualcomm IPQ Options" + +comment "Kernel build options" + +choice KERNEL_IPQ_MEM_PROFILE + prompt "IPQ memory profile" + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_edimax_cax1800 + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_compex_wpq87 + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4200v1 + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_redmi_ax6 + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_xiaomi_ax3600 + default IPQ_MEM_PROFILE_512 if TARGET_qualcommax_ipq807x_DEVICE_zte_mf269 + + config IPQ_MEM_PROFILE_1024 + bool "1024" + config IPQ_MEM_PROFILE_512 + bool "512" + config IPQ_MEM_PROFILE_256 + bool "256" +endchoice + +config KERNEL_IPQ_MEM_PROFILE + int + default 1024 if IPQ_MEM_PROFILE_1024 + default 512 if IPQ_MEM_PROFILE_512 + default 256 if IPQ_MEM_PROFILE_256 + + help + This option select memory profile to used, which defines + the reserved memory configuration used in device tree + +config KERNEL_SKB_RECYCLER + bool "Generic SKB recycling" + default y if KERNEL_IPQ_MEM_PROFILE > 256 + +choice KERNEL_SKB_RECYCLE_SIZE + prompt "SKB recycler size" + default SKB_RECYCLE_SIZE_2304 + default SKB_RECYCLE_SIZE_1856 if KERNEL_IPQ_MEM_PROFILE < 1024 + + depends on KERNEL_SKB_RECYCLER + config SKB_RECYCLE_SIZE_1856 + bool "1856" + config SKB_RECYCLE_SIZE_2304 + bool "2304" +endchoice + +config KERNEL_SKB_RECYCLE_SIZE + int + depends on KERNEL_SKB_RECYCLER + default 2304 if SKB_RECYCLE_SIZE_2304 + default 1856 if SKB_RECYCLE_SIZE_1856 + + help + SKB recycler default size + +config KERNEL_SKB_RECYCLER_MULTI_CPU + bool "Cross-CPU recycling for CPU-locked workloads" + depends on KERNEL_SKB_RECYCLER + default y + +config KERNEL_SKB_RECYCLER_PREALLOC + bool "Enable preallocation of SKBs (For SFE not NSS)" + depends on KERNEL_SKB_RECYCLER + default n + help + NOTE: This is primarily for platforms utilizing SFE (Shortcut Forwarding Engine) + and not NSS (Network Subsystem) offload. + + NSS offload platforms mostly utilize the SKB recycling feature when managing memory. + Mostly when to "reclaim" it. + + This option enables preallocation of SKBs. + The default is '16384' which means 16384 x 4096 (Size of SKB) = 64MB. + +config KERNEL_SKB_RECYCLE_MAX_PREALLOC_SKBS + int "Number of SKBs to be preallocated" + depends on KERNEL_SKB_RECYCLER_PREALLOC + default 16384 + +config KERNEL_SKB_FIXED_SIZE_2K + bool "SKB size fixed at 2K" + default n + default y if KERNEL_IPQ_MEM_PROFILE < 512 + help + SKB size fixed at 2K. Primarily for platforms with less than 512MB memory. + +config KERNEL_ALLOC_SKB_PAGE_FRAG_DISABLE + depends on KERNEL_SKB_RECYCLER + bool "Disable alloc skb page frag" + default n + +choice KERNEL_PREEMPTION + prompt "Kernel preemption level" + default KERNEL_PREEMPT + +config KERNEL_PREEMPT_NONE + bool "No Forced Preemption (Server)" + help + This is the traditional Linux preemption model, geared towards + throughput. It will still provide good latencies most of the time, + but there are no guarantees and occasional longer delays are + possible. + + Select this option if you are building a kernel for a server or + scientific/computation system, or if you want to maximize the + raw processing power of the kernel, irrespective of scheduling + latencies. + +config KERNEL_PREEMPT_NONE_BUILD + depends on KERNEL_PREEMPT_NONE + bool + default y + +config KERNEL_PREEMPT + bool "Preemptible Kernel (Low-Latency)" + help + This option reduces the latency of the kernel by making + all kernel code (that is not executing in a critical section) + preemptible. This allows reaction to interactive events by + permitting a low priority process to be preempted involuntarily + even if it is in kernel mode executing a system call and would + otherwise not be about to reach a natural preemption point. + This allows applications to run more 'smoothly' even when the + system is under load, at the cost of slightly lower throughput + and a slight runtime overhead to kernel code. + + Select this if you are building a kernel for a desktop or + embedded system with latency requirements in the milliseconds + range. + +if KERNEL_PREEMPT + config KERNEL_PREEMPT_RCU + bool "Preemptible RCU" + default y + select KERNEL_PREEMPT_COUNT + help + This option selects the RCU implementation that is + designed for very large SMP systems with hundreds or + thousands of CPUs, but for which real-time response + is also required. It also scales down nicely to + smaller systems. + + Select this option if you are unsure. + +endif +endchoice + +endmenu diff --git a/feeds.conf.default b/feeds.conf.default index 581de056c41e3e..5dac9fa6825d2c 100644 --- a/feeds.conf.default +++ b/feeds.conf.default @@ -2,3 +2,5 @@ src-git packages https://git.openwrt.org/feed/packages.git;openwrt-24.10 src-git luci https://git.openwrt.org/project/luci.git;openwrt-24.10 src-git routing https://git.openwrt.org/feed/routing.git;openwrt-24.10 src-git telephony https://git.openwrt.org/feed/telephony.git;openwrt-24.10 +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 diff --git a/nss-setup/config-nss.seed b/nss-setup/config-nss.seed new file mode 100644 index 00000000000000..db499bda7cb364 --- /dev/null +++ b/nss-setup/config-nss.seed @@ -0,0 +1,248 @@ +# vi: set syntax=make: + +# This builds for all ipq807x targets. +# To use this config, you must build from https://github.com/qosmio/openwrt-ipq + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~IMPORTANT~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Use `scripts/env` to create version controlled build environments. +# This ensures you can easily reproduce builds and view changes +# made during `make menuconfig`. +# +# It will also version control custom files you add to `files/` + +# To create a new environment, run: +# (e.g `./scripts/env new nss`) +# For more information, see `./scripts/env help` +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# 1. Copy this config to root of build folder name it ".config" +# `cp nss-setup/config-nss.seed .config` +# 2. Uncomment the device you want to build for listed below, replace 'is not set' with '=y' +# (e.g. CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4300=y) +# 3. Update feeds +# `./scripts/feeds update -a && ./scripts/feeds install -a` +# 4. Expand your seed file to a full config +# `make defconfig V=s` +# 5. If you want to make further customizations, use `make menuconfig` +# 6. Build the firmware +# `make -j$(nproc) V=s` + +# use `make menuconfig` to further customize building just for your target or adding custom packages. + +##################################################################### +# Target platform +##################################################################### +CONFIG_TARGET_qualcommax=y +CONFIG_TARGET_qualcommax_ipq807x=y + +##################################################################### +# Target device +##################################################################### +# Uncomment target device you want to build for, set '=y' + +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_arcadyan_aw1000 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_asus_rt-ax89x is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_buffalo_wxr-5950ax12 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_cmcc_rm2-6 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_compex_wpq873 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_dynalink_dl-wrx36 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_edgecore_eap102 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_edimax_cax1800 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4200v1 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4200v2 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx4300 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx5300 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_linksys_mx8500 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_rax120v2 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_sxr80 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_sxs80 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_wax218 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_wax620 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_netgear_wax630 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_prpl_haze is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_qnap_301w is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_redmi_ax6 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_spectrum_sax1v1k is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_xiaomi_ax3600 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_xiaomi_ax9000 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_yuncore_ax880 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_zbtlink_zbt-z800ax is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_zte_mf269 is not set +# CONFIG_TARGET_qualcommax_ipq807x_DEVICE_zyxel_nbg7815 is not set + +##################################################################### +# NSS Offloading +##################################################################### +CONFIG_ATH11K_NSS_SUPPORT=y +CONFIG_ATH11K_NSS_MESH_SUPPORT=y +CONFIG_PACKAGE_MAC80211_NSS_SUPPORT=y +CONFIG_PACKAGE_kmod-qca-nss-drv=y +CONFIG_PACKAGE_kmod-qca-nss-drv-bridge-mgr=y +CONFIG_PACKAGE_kmod-qca-nss-ecm=y + +# Additional NSS packages (VLAN, Multicast Snooping, etc) +CONFIG_PACKAGE_kmod-qca-nss-drv-vlan-mgr=y +CONFIG_PACKAGE_kmod-qca-mcs=y + +# NSS SQM Traffic Shaping +CONFIG_PACKAGE_sqm-scripts-nss=y + +##################################################################### +# Compiler Optimization +##################################################################### +CONFIG_BUILD_PATENTED=y +# Use ccache to speed up subsequent builds +CONFIG_CCACHE=y +CONFIG_DEVEL=y +CONFIG_EXPERIMENTAL=y +CONFIG_TOOLCHAINOPTS=y +CONFIG_TARGET_OPTIONS=y +CONFIG_TARGET_OPTIMIZATION="-O2 -pipe -mcpu=cortex-a53+crc+crypto" +CONFIG_USE_GC_SECTIONS=y + +# If your device supports USB and plan on installing packages via opkg +# Use a USB device, as these devices have limited storage and you avoid wearing out the internal flash. +# We update the PATH to include the USB device. '/opt' is a common mount point. +CONFIG_TARGET_INIT_PATH="/usr/sbin:/usr/bin:/sbin:/bin:/opt/usr/bin:/opt/bin:/opt/sbin:/opt/usr/sbin" + +# Kernel Config +CONFIG_COLLECT_KERNEL_DEBUG=y +CONFIG_KERNEL_PERF_EVENTS=y +CONFIG_KERNEL_DYNAMIC_DEBUG=y +CONFIG_KERNEL_ARM_PMU=y +CONFIG_KERNEL_ARM_PMUV3=y +CONFIG_KERNEL_PREEMPT_NONE=y +CONFIG_KERNEL_PREEMPT_NONE_BUILD=y + +# Reduce kernel module size +# Disable unnecessary debugging for Wi-Fi driver. +CONFIG_ATH11K_DEBUGFS_HTT_STATS=n +CONFIG_ATH11K_DEBUGFS_STA=n +# Disables thermal throttling support for ath11k. +CONFIG_ATH11K_THERMAL=n + +##################################################################### +# SSL Configuration +##################################################################### +# Use OpenSSL as the preferred SSL library +CONFIG_PACKAGE_libustream-openssl=y +CONFIG_PACKAGE_libustream-mbedtls=n +CONFIG_PACKAGE_libopenssl=y +CONFIG_LUA_ECO_OPENSSL=y +CONFIG_LUA_ECO_MBEDTLS=n +# Optimize OpenSSL for speed over size +CONFIG_OPENSSL_OPTIMIZE_SPEED=y + +##################################################################### +# Wireless Configuration +##################################################################### +# Enable WPA3 and Mesh support +CONFIG_PACKAGE_wpad-mesh-openssl=y +# Avoid using mbedTLS for consistency across packages and to avoid mixed SSL libraries. +CONFIG_PACKAGE_wpad-basic-mbedtls=n + +##################################################################### +# Library Optimization +##################################################################### +# Optimize common libraries (zlib, zstd) for speed, improving performance on compression tasks. +CONFIG_ZLIB_OPTIMIZE_SPEED=y +CONFIG_ZSTD_OPTIMIZE_O3=y + +##################################################################### +# Feeds Configuration +##################################################################### +# Prevent opkg from adding custom feeds to /etc/opkg/distfeeds.conf in the final image. +CONFIG_FEED_nss_packages=n +CONFIG_FEED_sqm_scripts_nss=n + +# --- Essential Packages --- + +##################################################################### +# LUCI (the web interface) +##################################################################### +CONFIG_PACKAGE_luci=y +# Enable HTTPS support +CONFIG_PACKAGE_luci-ssl-openssl=y + +##################################################################### +# LUCI Applications +##################################################################### +# - Firewall: Manage firewall rules via LUCI. +CONFIG_PACKAGE_luci-app-firewall=y +# - OPKG: Package manager for OpenWRT, manage installed packages via LUCI. +CONFIG_PACKAGE_luci-app-opkg=y + +# - iperf3: CLI tool to measure network performance. +# This is essential to test WiFi speeds, +# as posting speedtest.net results is useless +# without knowing the network conditions. +CONFIG_PACKAGE_iperf3=y + +# --- Optional Packages --- # + +#### EVERYTHING BELOW THIS SECTION IS OPTIONAL!! + +#### You can delete or comment out with '#' any package you don't want to install. + +##################################################################### +# LUCI Applications +##################################################################### + +# - SQM: Smart Queue Management for bufferbloat control. +CONFIG_PACKAGE_luci-app-sqm=y +# - Statistics: Monitor your router’s performance (CPU, memory, bandwidth). +CONFIG_PACKAGE_luci-app-statistics=y +# - ACME: Automated SSL cert management. If you want to access your router via HTTPS and have a domain. +CONFIG_PACKAGE_luci-app-acme=y +# - Watchcat: Automate reboots on connection loss. +CONFIG_PACKAGE_luci-app-watchcat=y +# - WireGuard: VPN support. Will also select the kernel module. +CONFIG_PACKAGE_luci-proto-wireguard=y +# - NLBWMon: Network usage monitoring to track bandwidth by host. +CONFIG_PACKAGE_luci-app-nlbwmon=y + +##################################################################### +# Kernel Modules +##################################################################### +# Filesystem support for USB storage: +# FAT32: Useful to load recovery or new images that can be booted by u-boot +CONFIG_PACKAGE_kmod-fs-vfat=y +# NTFS: Mostly useful for Windows users. +CONFIG_PACKAGE_kmod-fs-ntfs3=y + +# Network +# Bridge module support for working with nftables for more complex firewall setups. +CONFIG_PACKAGE_kmod-nft-bridge=y + +# USB Storage +# NOTE: Not all IPQ807x devices have USB ports, so this is optional. +CONFIG_PACKAGE_kmod-usb-storage=y + +# Logging/Debugging + +# ramoops: kernel module that logs crashes to RAM which can be read after a reboot. +# Check '/sys/fs/pstore' for logs after a crash. +CONFIG_PACKAGE_kmod-ramoops=y + +##################################################################### +# Packages +##################################################################### + +# - curl: CLI tool to transfer data with URLs. Useful for scripting, and supperior to wget. +CONFIG_PACKAGE_curl=y +CONFIG_LIBCURL_OPENSSL=y +CONFIG_LIBCURL_MBEDTLS=n +# - rsync: Efficient file transfers and backups. +CONFIG_PACKAGE_rsync=y +# - jq: Parse JSON data from the command line. Useful for scripting, and WAY better than `jsonfilter`. +CONFIG_PACKAGE_jq=y +# - pigz: Parallel gzip for faster compression. +CONFIG_PACKAGE_pigz=y +# - tar: Archiving utility. The default busybox tar is very limited. +CONFIG_PACKAGE_tar=y +# - tcpdump: Capture and analyze network traffic. +CONFIG_PACKAGE_tcpdump=y +# - htop: CLI tool to monitor system resource usage. +CONFIG_PACKAGE_htop=y +# lm-sensors isn't needed for IPQ807x devices. +CONFIG_HTOP_LMSENSORS=n diff --git a/nss-setup/example/01-mesh-node b/nss-setup/example/01-mesh-node new file mode 100644 index 00000000000000..1d1699aa5f3b22 --- /dev/null +++ b/nss-setup/example/01-mesh-node @@ -0,0 +1,87 @@ + +config wifi-device 'radio0' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi' + option band '5g' + option txpower '21' + option country 'US' + option htmode 'HE80' + option channel '64' + option cell_density '0' + option noscan '1' + +config wifi-device 'radio1' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+1' + option band '2g' + option txpower '24' + option country 'US' + option htmode 'HE20' + option channel '1' + option cell_density '0' + +config wifi-device 'radio2' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+2' + option band '5g' + option txpower '30' + option country 'US' + option htmode 'HE80' + option channel '161' + option cell_density '0' + option noscan '1' + +config wifi-iface '5g' + option device 'radio1' + option mode 'ap' + option network 'lan' + option ssid 'MX4300' + option encryption 'sae-mixed' + option key 'xxxxxxxxxxxxx' + option beacon_int '97' + option bss_transition '1' + option dtim_period '3' + option ft_over_ds '0' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option ocv '0' + +config wifi-iface '2g' + option device 'radio1' + option mode 'ap' + option network 'lan' + option ssid 'MX4300' + option encryption 'sae-mixed' + option key 'xxxxxxxxxxxxx' + option bss_transition '1' + option dtim_period '3' + option ft_over_ds '0' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option max_inactivity '86400' + option ocv '0' + +config wifi-iface 'mesh' + option device 'radio2' + option encryption 'sae' + option key '4b22dd0c95846d36a8760ec90b703601c60f31ce4c8db1d9ade683cd3a2c2326' + option mesh_id 'MX4300-MESH' + option mode 'mesh' + option network 'lan' + option mesh_fwding '1' + option mesh_gate_announcements '1' + option mesh_hwmp_rootmode '4' + option mesh_max_peer_links '5' + option mesh_ttl '5' + option mesh_element_ttl '3' + option mesh_hwmp_max_preq_retries '2' + option mesh_rssi_threshold '-70' + option disabled '0' diff --git a/nss-setup/example/02-mesh-sat-node b/nss-setup/example/02-mesh-sat-node new file mode 100644 index 00000000000000..17f639535e31fb --- /dev/null +++ b/nss-setup/example/02-mesh-sat-node @@ -0,0 +1,87 @@ + +config wifi-device 'radio0' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi' + option band '5g' + option txpower '21' + option country 'US' + option htmode 'HE80' + option channel '64' + option cell_density '0' + option noscan '1' + +config wifi-device 'radio1' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+1' + option band '2g' + option txpower '24' + option country 'US' + option htmode 'HE20' + option channel '1' + option cell_density '0' + +config wifi-device 'radio2' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+2' + option band '5g' + option txpower '30' + option country 'US' + option htmode 'HE80' + option channel '161' + option cell_density '0' + option noscan '1' + +config wifi-iface '5g' + option device 'radio1' + option mode 'ap' + option network 'lan' + option ssid 'MX4300' + option encryption 'sae-mixed' + option key 'xxxxxxxxxxxxx' + option beacon_int '97' + option bss_transition '1' + option dtim_period '3' + option ft_over_ds '0' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option ocv '0' + +config wifi-iface '2g' + option device 'radio1' + option mode 'ap' + option network 'lan' + option ssid 'MX4300' + option encryption 'sae-mixed' + option key 'xxxxxxxxxxxxx' + option bss_transition '1' + option dtim_period '3' + option ft_over_ds '0' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option max_inactivity '86400' + option ocv '0' + +config wifi-iface 'mesh' + option device 'radio2' + option encryption 'sae' + option key '4b22dd0c95846d36a8760ec90b703601c60f31ce4c8db1d9ade683cd3a2c2326' + option mesh_id 'MX4300-MESH' + option mode 'mesh' + option network 'lan' + option mesh_fwding '0' + option mesh_gate_announcements '0' + option mesh_hwmp_rootmode '0' + option mesh_max_peer_links '3' + option mesh_ttl '5' + option mesh_element_ttl '3' + option mesh_hwmp_max_preq_retries '2' + option mesh_rssi_threshold '-70' + option disabled '0' diff --git a/nss-setup/example/03-uci-defaults b/nss-setup/example/03-uci-defaults new file mode 100644 index 00000000000000..509af373947418 --- /dev/null +++ b/nss-setup/example/03-uci-defaults @@ -0,0 +1,464 @@ +#!/bin/sh -e +# shellcheck disable=2034,2091,3010,3037,3060 shell=busybox +# Custom UCI defaults script for Linksys MX4200/4300/5300 etc +# Create folder "files/etc/uci-defaults/" in build root and copy this script there. +# Customize to your needs. + +# Uncomment the following line to capture all output to a log file +# exec > /root/uci-defaults.log 2>&1 + +# We will also use this MAC to set br-lan, and lan ports 1,2,3. +# That is how they are supposed to be but there's an issue on MX4300 +# where the MAC is not set correctly for lan 2 and 3 and randomly changes each boot. +mac=$(fw_printenv -n ethaddr | tr '[:upper:]' '[:lower:]') + +# Set to '0' to enable WDS and disable mesh +wds_disable=1 +bridge_mode=true + +# radio_5g_low is the low 5G band (36-64) +# radio_5g_high is the high 5G band (100-165) +radio_5g_low="radio0" +radio_2g="radio1" +radio_5g_high="radio2" + +ap_5g_radio="${radio_5g_low}" +ap_2g_radio="${radio_2g}" +mesh_radio="${radio_5g_high}" +wds_radio="${radio_5g_high}" + +# for 80mhz channel 161 will use channels 149-161 (leave as is) +mesh_channel=161 +# for 80mhz channel 64 will use channels 52-64 (change below for specific devices if needed) +ap_5g_channel=64 +ap_2g_channel=6 + +ap_2g_txpower=24 +ap_5g_txpower=21 +mesh_txpower=30 + +# Unique UCI config names for each section +ap_5g_iface="ap_5g" +ap_2g_iface="ap_2g" +mesh_iface="mesh" +wds_iface="wds" + +# You should start customizing from here +# Must be the same SSID for both 2G and 5G for 802.11 k/v/r +ap_2g_ssid="OpenWrt" +ap_5g_ssid="${ap_2g_ssid}" + +mesh_id="OpenWrt-Mesh" +wds_ssid="OpenWrt-WDS" + +ap_key="SOME_KEY" +mesh_gate_key="SOME_LONG_RANDOM_KEY" +wds_key="${mesh_gate_key}" + +country="US" +timezone="EST5EDT,M3.2.0,M11.1.0" +zonename="America/New York" + +router="192.168.1.1" +netmask=24 +lan_proto="static" +prefix="mx4300" + +# If node is setup to use DHCP and fails to get an IP, +# a cable connection can be used to login and manage it. +# You'll need to set a static IP on your computer in the same subnet. +# (i.e. 10.1.1.2/24) and connect to the fallback management IP. +fallback_mgmt_ip="10.1.1.1" + +# In case you want to reset firmware in future, but want to use different mesh band +# use `fw_setenv mesh_band low_5g` to use "radio0" (low 5G band) (36-64) +# use `fw_setenv mesh_band high_5g` to use "radio2" (high 5G band) (100-165) +# This will then be used to set the channel for the mesh interface. +# Default is high_5g (radio2) (channel 161) +# NOTE: ON MX4300 ONLY THE 2ND RADIO WORKS WITH MESH. LEAVE THIS UNCHANGED. +mesh_band="$(fw_printenv -n mesh_band 2> /dev/null)" +mesh_band="${mesh_band:-high_5g}" + +if [ "$mesh_band" = "low_5g" ]; then + mesh_radio="${radio_5g_low}" + wds_radio="${radio_5g_low}" + mesh_channel="64" + ap_5g_radio="${radio_5g_high}" + ap_5g_channel="161" +fi + +# Setup satellite nodes to simply extend wifi coverage from the main router. +# This avoids "daisy chaining" traffic through multiple nodes. +# This usecase covers 99% of typical home setups. +mesh_gate_announcements="0" +mesh_hwmp_rootmode="0" +mesh_fwding="0" +mesh_rssi_threshold="-65" + +stp_priority=8192 + +# Only version version 11.4.0.5 has mesh offload support, so disable and use WDS instead +if ! grep -q NSS.HK.11.4.0.5 /lib/firmware/qca-nss0-retail.bin 2> /dev/null; then + wds_disable=0 +fi + +# For Linksys MX4200/4300/5300 etc, only need to match the first 5 bytes +# of the MAC address to determine it's the same device. +# Replace 'xx:xx' with the one found on the bottom of your device. +# NOTE: '80:69:1a' is just one of the OUI for Linksys. Yours may be different. +# i.e. 'D8:EC:5E:xx:xx', or others. We just need the first 10 characters. +# Add a new `elif` block for each device if needed. +if [[ "${mac}" =~ "80:69:1a:xx:xx" ]]; then + suffix=2 + # If the node is in bridge mode and connected to a router via ethernet, or itself is a router. + # set the following for gate announcements, rootmode and forwarding. + mesh_gate_announcements=1 + mesh_hwmp_rootmode=2 + mesh_fwding=1 + # Only ONE of the nodes should be in ap mode if setting up WDS. + wds_mode=ap +elif [[ "${mac}" =~ "80:69:1a:xx:xx" ]]; then + suffix=3 + wds_mode=sta + # it's a good idea to spread out the channels on the AP + # to ensure they don't interfere with each other. + # This is especially important if you have multiple APs in close proximity. + # For 80mhz channel 48 will use channels 36-48 + ap_5g_channel=48 + ap_2g_channel=1 +else + lan_proto="dhcp" +fi + +if { [ "$lan_proto" = "dhcp" ] && [ -z "$suffix" ]; } || [ -z "$suffix" ] ; then + # get last 2 octets of the MAC address + # The final hostname will be "${prefix}-${suffix}", e.g. "mx4300-9efc" + # otherwise, the suffix will be you specified above. + suffix="${mac#*:*:*:*:}" + suffix="${suffix/:/}" +else + # If bridge mode : true + # and router IP is : 192.168.1.1 + # and suffix is : 2 + # then the IP will be : 192.168.1.2 + if $bridge_mode; then + ipaddr="${router%.*}.${suffix}" + else + # otherwise assume the node is a router and set the IP to the router IP specified above. + # If you DO disable bridge mode for a device, make sure you ONLY do it for ONE device. + ipaddr="${router}" + stp_priority=0 + fi +fi + +hostname="${prefix}-${suffix}" + +uci import <<- EOF +package system + +config system + option hostname '${hostname}' + option timezone '${timezone}' + option ttylogin '0' + option log_size '128' + option urandom_seed '1' + option zonename '${zonename}' + option cronloglevel '9' + +config timeserver 'ntp' + option enable_server '1' + option interface 'lan' + $(${bridge_mode} && echo list server ''${router}'') + # The following NTP servers are NIST Internet Time Servers. + # Change them to suit your needs/regional location. + list server '129.6.15.28' + list server '129.6.15.29' + list server '129.6.15.30' + list server '2610:20:6f15:15::27' + list server '2610:20:6f15:15::28' + list server '129.6.15.27' + list server '129.6.15.26' + +config led + option name 'Blue Off' + option sysfs 'blue:status' + option trigger 'none' + option default '0' + +config led + option name 'Red Off' + option sysfs 'red:status' + option trigger 'none' + option default '0' +EOF + +# satellite nodes should not have any DHCP/DNS services running. +# Nor should they have any firewall/dnsmasq rules. + +${bridge_mode} && { + + # Disable services services in case we are running as dumb ap + for prog in firewall sqm unbound adblock-fast banip; do + CMD=/etc/init.d/${prog} + if [ -r ${CMD} ]; then + ${CMD} disable + fi + done + + [ -r /etc/hotplug.d/ntp/25-unbound ] && rm /etc/hotplug.d/ntp/25-unbound + + uci import <<- EOF > /dev/null +package dhcp + +config dnsmasq + option boguspriv '0' + option rebind_protection '0' + option domain 'lan' + option expandhosts '1' + option readethers '1' + option leasefile '/tmp/dhcp.leases' + option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto' + option localservice '0' + option ednspacket_max '1232' + +config dhcp 'lan' + option interface 'lan' + option dhcpv4 'disabled' + option dhcpv6 'disabled' + option ignore '1' + option dynamicdhcp '0' + +config odhcpd 'odhcpd' + option maindhcp '0' + option leasefile '/tmp/hosts/odhcpd' + option leasetrigger '/usr/sbin/odhcpd-update' + option loglevel '4' + +EOF + + uci import <<- EOF > /dev/null +package network + +config globals 'globals' + +config interface 'loopback' + option device 'lo' + option proto 'static' + option ipaddr '127.0.0.1' + option netmask '255.0.0.0' + +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1' + list ports 'lan2' + list ports 'lan3' + option macaddr '${mac}' + option stp '1' + option igmp_snooping '1' + option arp_accept '1' + option priority '${stp_priority}' + +config interface 'lan' + option device 'br-lan' +$( + if [ "$lan_proto" = "static" ]; then + cat <<- EOD + option proto 'static' + list ipaddr '${ipaddr}/${netmask:-24}' + list dns '${router}' + option gateway '${router}' + option delegate '0' +EOD + else + cat <<- EOD + option proto 'dhcp' + option delegate '0' + +config interface 'mgmt' + option device '@lan' + option proto 'static' + option ipaddr '${fallback_mgmt_ip}' + option netmask '255.255.255.0' + option delegate '0' +EOD + fi +) + +config interface 'lan6' + option device '@lan' + option proto 'dhcpv6' + option reqaddress 'try' + option reqprefix 'no' + option delegate '0' + +config device + option name 'lan1' + option macaddr '${mac}' + +config device + option name 'lan2' + option macaddr '${mac}' + +config device + option name 'lan3' + option macaddr '${mac}' +EOF + +# Sometimes nodes may not be able to reach the gateway for whatever reason +# Since they will be connected via wifi it's cumbersome having to hardwire just to troubleshoot +# Install the `watchcat` package to automatically reboot the node if it can't reach the gateway +uci import <<- EOF > /dev/null + +package watchcat + +config watchcat + option period '5m' + option mode 'ping_reboot' + option pinghosts '${router}' + option addressfamily 'any' + option pingperiod '10s' + option pingsize 'standard' + option forcedelay '1m' +EOF +} + +# If not in bridge mode, then assume setting up as a router +${bridge_mode} || { + uci batch <<- EOF > /dev/null + set network.lan.proto='static' + del_list network.lan.ipaddr + set network.lan.ipaddr='${router}/${netmask:-24}' +EOF +} + +uci import <<- EOF +package wireless + +config wifi-device '${radio_5g_low}' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi' + option band '5g' + option txpower '${ap_5g_txpower}' + option country '${country:-US}' + option htmode 'HE80' + option channel '${ap_5g_channel:-64}' + option cell_density '0' + option noscan '1' + +config wifi-device '${radio_2g}' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+1' + option band '2g' + option txpower '${ap_2g_txpower}' + option country '${country:-US}' + option htmode 'HE20' + option channel '${ap_2g_channel:-6}' + option cell_density '0' + option noscan '1' + +config wifi-device '${radio_5g_high}' + option type 'mac80211' + option path 'platform/soc@0/c000000.wifi+2' + option band '5g' + option txpower '${mesh_txpower}' + option country '${country:-US}' + option htmode 'HE80' + option channel '${mesh_channel:-161}' + option cell_density '0' + option noscan '1' + +config wifi-iface '${ap_5g_iface}' + option device '${ap_5g_radio}' + option mode 'ap' + option network 'lan' + option ssid '${ap_5g_ssid}' + option encryption 'psk2+ccmp' + option key '${ap_key}' + option beacon_int '97' + option bss_transition '1' + option dtim_period '3' + option ft_over_ds '0' + option ft_psk_generate_local '1' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option reassociation_deadline '20000' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option wpa_group_rekey '86400' + +config wifi-iface '${ap_2g_iface}' + option device '${ap_2g_radio}' + option mode 'ap' + option network 'lan' + option ssid '${ap_2g_ssid}' + option encryption 'psk2+ccmp' + option key '${ap_key}' + option bss_transition '1' + option beacon_int '100' + option dtim_period '3' + option ft_over_ds '0' + option ft_psk_generate_local '1' + option ieee80211r '1' + option ieee80211k '1' + option proxy_arp '1' + option reassociation_deadline '20000' + option time_advertisement '2' + option time_zone 'GMT0' + option wnm_sleep_mode '1' + option wpa_group_rekey '86400' + option max_inactivity '4260' + +config wifi-iface '${mesh_iface}' + option device '${mesh_radio}' + option encryption 'sae' + option key '${mesh_gate_key}' + option mesh_id '${mesh_id}' + option mode 'mesh' + option network 'lan' + option mesh_fwding '${mesh_fwding:-0}' + option mesh_gate_announcements '${mesh_gate_announcements:-0}' + option mesh_hwmp_rootmode '${mesh_hwmp_rootmode:-0}' + option mesh_max_peer_links '5' + option mesh_ttl '5' + option mesh_element_ttl '3' + option mesh_hwmp_max_preq_retries '2' + option mesh_rssi_threshold '${mesh_rssi_threshold}' + option disabled $([ ${wds_disable:-1} -eq 1 ] && echo '0' || echo '1') + +config wifi-iface '${wds_iface}' + option device '${wds_radio}' + option mode '${wds_mode:-ap}' + option network 'lan' + option ssid '${wds_ssid}' + option encryption 'psk2+ccmp' + option key '${wds_key}' + option wds '1' + option disabled '${wds_disable:-1}' + $([ "${wds_mode:-ap}" = "ap" ] && echo "option hidden '1'") +EOF + +uci batch <<- EOF + set wireless.${mesh_radio}.channel='${mesh_channel}' + set wireless.${ap_5g_radio}.channel='${ap_5g_channel}' + + set wireless.${mesh_radio}.cell_density='0' + set wireless.${ap_5g_radio}.cell_density='0' + + set wireless.${mesh_iface}.device='${mesh_radio}' + set wireless.${wds_iface}.device='${mesh_radio}' + set wireless.${ap_5g_iface}.device='${ap_5g_radio}' +EOF + +uci changes + +uci commit system +uci commit dhcp +uci commit network +uci commit wireless + +fw_setenv mesh_band "${mesh_band}" + +exit 0 diff --git a/nss-setup/example/04-vlans b/nss-setup/example/04-vlans new file mode 100644 index 00000000000000..221460f5ff1b14 --- /dev/null +++ b/nss-setup/example/04-vlans @@ -0,0 +1,166 @@ +# For NSS setups, you CANNOT enable the option for vlan filtering. +# This is done internally by NSS (via `kmod-qca-nss-drv-vlan-mgr`). +# +# Check if you have it enabled by running the following command: +# `uci show network | grep vlan_filtering` +# +# If it's enabled, you might see: +# `network.@device[0].vlan_filtering='1'` +# +# Disable it by running the following commands: +# `uci del network.@device[0].vlan_filtering` +# `uci commit network` +# `service network restart` (or reboot the device) +# +# You also cannot use tagging in the typical way, as the NSS does not support it. +# That means your config must not anything like: +# +# 'list ports 'lan1:u*' +# 'list ports 'lan2:t' +# 'list ports 'lan3:t' +# +# To set up VLANs you must: +# 1.) Set them up on specific ports +# 2.) Bridge them into interfaces (they can be left unmanaged) +# 3.) Create firewall rules + +# Example setup: +# 1.) Primary Network (vlan10) - untagged +# 2.) Guest Network (vlan30) +# 3.) IoT Network (vlan40) + +# /etc/config/network +config interface 'loopback' + option device 'lo' + option proto 'static' + option ipaddr '127.0.0.1' + option netmask '255.0.0.0' + +config globals 'globals' + option ula_prefix 'fd32:aa0c:9a35::/48' + +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1' + list ports 'lan2' + list ports 'lan3' + list ports 'lan4' + list ports 'wan' + option igmp_snooping '1' + +config device + option name 'lan1' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan2' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan3' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan4' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config interface 'lan' + option device 'br-lan' + option proto 'static' + option ip6assign '60' + list ipaddr '192.168.1.1/24' + option force_link '0' + +config device + option type '8021q' + option ifname 'wan' + option vid '30' + option name 'wan.30' + +config device + option type '8021q' + option ifname 'wan' + option vid '40' + option name 'wan.40' + +config device + option type 'bridge' + option name 'br-iot' + list ports 'wan.40' + option igmp_snooping '1' + +config device + option type 'bridge' + option name 'br-guest' + list ports 'wan.30' + option igmp_snooping '1' + +config interface 'guest' + option proto 'none' + option device 'br-guest' + +config interface 'iot' + option proto 'none' + option device 'br-iot' + +# /etc/config/wireless + +config wifi-iface 'default_radio0'' + option device 'radio0' + option mode 'ap' + option network 'lan' + option ssid 'OpenWrt' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' + +config wifi-iface 'guest' + option device 'radio0' + option mode 'ap' + option network 'br-guest' + option ssid 'OpenWrt-Guest' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' + +config wifi-iface 'iot' + option device 'radio0' + option mode 'ap' + option network 'br-iot' + option ssid 'OpenWrt-IoT' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' + +# For creating VLANs on physical ports with a VLAN for DMZ on ports 3 and 4 (lan3,lan4) +# Change the following: +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1' + list ports 'lan2' + list ports 'wan' + +config device + option type '8021q' + option ifname 'wan' + option vid '30' + option name 'dmz.30' + +config device + option type 'bridge' + option name 'br-dmz' + list ports 'dmz.30' + list ports 'lan3' + list ports 'lan4' + +config interface 'dmz' + option proto 'none' + option device 'br-dmz' diff --git a/nss-setup/example/README.md b/nss-setup/example/README.md new file mode 100644 index 00000000000000..a358e118646730 --- /dev/null +++ b/nss-setup/example/README.md @@ -0,0 +1,288 @@ +--- +# **Configuring VLANs with OpenWRT on NSS-Enabled Devices** +### **Important Note:** No VLAN Filtering for NSS Devices + +For **NSS setups**, you **cannot** enable VLAN filtering directly because **NSS handles VLAN internally** through the module `kmod-qca-nss-drv-vlan-mgr`. +Enabling VLAN filtering via the standard method will interfere with the NSS. To check whether it's enabled, follow the steps below. + +--- +### 1. **Check if VLAN Filtering is Enabled** +Run the following command to check if VLAN filtering is active: +```vim +uci show network | grep vlan_filtering +``` +If you see a line like this: +```vim +network.@device[0].vlan_filtering='1' +``` +VLAN filtering is enabled and you need to **disable it**. Follow the next steps. + +### 2. **Disable VLAN Filtering** +## **VLAN Tagging Restrictions** +To disable VLAN filtering, run: +```vim +uci del 'network.@device[0].vlan_filtering' +uci commit network +service network restart +``` +Alternatively, you can reboot your device to apply the changes: +```vim +reboot +``` +--- +## **VLAN Tagging Restrictions** + +NSS does not support VLAN tagging in the usual way. This means **you can't use tags like `u*` (untagged) or `t` (tagged)** in your configuration. + +**Example of what to avoid**: +```bash +list ports 'lan1:u*' +list ports 'lan2:t' +list ports 'lan3:t' +``` +--- + +## **How to Set Up VLANs on NSS-Enabled Devices** + +Instead of tagging, you'll need to follow a different approach. Here's a step-by-step process: + +1. **Set up VLANs on specific ports.** +2. **Bridge these VLANs into interfaces** (you can leave them **unmanaged** if needed). +3. **Create firewall rules** to manage traffic between VLANs. + +--- + +## **Example VLAN Setup** + +Below is an example of how you can configure your VLANs on an OpenWRT router with **NSS support**. This setup includes: + +- A **Primary Network** on VLAN 10 (untagged). +- A **Guest Network** on VLAN 30. +- An **IoT Network** on VLAN 40. + +### **Network Configuration** +This is an example of `/etc/config/network`: +```vim +config interface 'loopback' + option device 'lo' + option proto 'static' + option ipaddr '127.0.0.1' + option netmask '255.0.0.0' + +config globals 'globals' + option ula_prefix 'fd32:aa0c:9a35::/48' + +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1' + list ports 'lan2' + list ports 'lan3' + list ports 'lan4' + list ports 'wan' + option igmp_snooping '1' + +config device + option name 'lan1' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan2' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan3' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config device + option name 'lan4' + option macaddr 'AA:BB:CC:DD:EE:FF' + +config interface 'lan' + option device 'br-lan' + option proto 'static' + option ip6assign '60' + list ipaddr '192.168.1.1/24' + option force_link '0' + +config device + option type '8021q' + option ifname 'wan' + option vid '30' + option name 'wan.30' + +config device + option type '8021q' + option ifname 'wan' + option vid '40' + option name 'wan.40' + +config device + option type 'bridge' + option name 'br-iot' + list ports 'wan.40' + option igmp_snooping '1' + +config device + option type 'bridge' + option name 'br-guest' + list ports 'wan.30' + option igmp_snooping '1' + +config interface 'guest' + option proto 'none' + option device 'br-guest' + +config interface 'iot' + option proto 'none' + option device 'br-iot' +``` +### **Explanation** + +- **Primary Network (VLAN 10)**: Connected through the LAN ports, untagged. +- **Guest Network (VLAN 30)**: Runs on a bridge called `br-guest` and spans across the **WAN** port tagged with VLAN ID 30. +- **IoT Network (VLAN 40)**: Runs on a bridge called `br-iot` and uses VLAN ID 40 on the **WAN** port. +--- + +## **WiFi Configuration** + +Here is an example of how to configure the WiFi interfaces for different networks in `/etc/config/wireless`: +```vim +config wifi-iface 'lan' + option device 'radio0' + option mode 'ap' + option network 'lan' + option ssid 'OpenWrt' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' + +config wifi-iface 'guest' + option device 'radio0' + option mode 'ap' + option network 'guest' + option ssid 'OpenWrt-Guest' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' + +config wifi-iface 'iot' + option device 'radio0' + option mode 'ap' + option network 'iot' + option ssid 'OpenWrt-IoT' + option encryption 'psk2' + option key '********' + option ocv '0' + option bss_transition '1' + option dtim_period '3' +``` +### **Explanation** + +- **LAN WiFi**: The `default_radio0` interface is associated with the **Primary Network** on **VLAN 10**. +- **Guest WiFi**: The `guest` interface is associated with the **Guest Network** on **VLAN 30**. +- **IoT WiFi**: The `iot` interface is associated with the **IoT Network** on **VLAN 40**. +--- + +## **Advanced VLAN Setup for DMZ** +To create a DMZ using **VLAN 30** on **LAN ports 3 and 4**, you can modify your configuration like this: +```vim +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1' + list ports 'lan2' + list ports 'wan' + +config device + option type '8021q' + option ifname 'wan' + option vid '30' + option name 'dmz.30' + +config device + option type 'bridge' + option name 'br-dmz' + list ports 'dmz.30' + list ports 'lan3' + list ports 'lan4' + +config interface 'dmz' + option proto 'none' + option device 'br-dmz' +``` +--- + +## **Advanced VLAN Setup with trunk ports** +Assuming other managed network devices need to be connected to your NSS-enabled router, you need to configure trunk ports. The setup in the example below consists of: + +- A **Primary Network** on VLAN 1. +- An **IoT Network** on VLAN 10. +- **LAN ports 1 and 2** set up as trunk ports for **VLAN 1 and 10** +- **LAN port 3** untagged and bridged into the **Primary Network** +- **LAN port 4** untagged and bridged into the **IoT Network** + +```vim +config interface 'lan' + option device 'br-lan' + option proto 'static' + option ipaddr '192.168.1.1' + option netmask '255.255.255.0' + option ip6assign '60' + +config interface 'iot' + option proto 'static' + option device 'br-iot' + option ipaddr '192.168.10.1' + option netmask '255.255.255.0' + +config interface 'wan' + option device 'wan' + option proto 'dhcp' + +config interface 'wan6' + option device 'wan' + option proto 'dhcpv6' + +config device + option type '8021q' + option ifname 'lan1' + option vid '1' + option name 'lan1.1' + +config device + option type '8021q' + option ifname 'lan2' + option vid '1' + option name 'lan2.1' + +config device + option type '8021q' + option ifname 'lan1' + option vid '10' + option name 'lan1.10' + +config device + option type '8021q' + option ifname 'lan2' + option vid '10' + option name 'lan2.10' + +config device + option name 'br-lan' + option type 'bridge' + list ports 'lan1.1' + list ports 'lan2.1' + list ports 'lan3' + +config device + option type 'bridge' + option name 'br-iot' + list ports 'lan1.10' + list ports 'lan2.10' + list ports 'lan4' +``` diff --git a/package/kernel/ath10k-ct/patches/999-001-fix-ath10k_update_vif_offload.patch b/package/kernel/ath10k-ct/patches/999-001-fix-ath10k_update_vif_offload.patch new file mode 100644 index 00000000000000..98c6459a924034 --- /dev/null +++ b/package/kernel/ath10k-ct/patches/999-001-fix-ath10k_update_vif_offload.patch @@ -0,0 +1,20 @@ +--- a/ath10k-6.10/mac.c ++++ b/ath10k-6.10/mac.c +@@ -6365,7 +6365,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; +@@ -6387,6 +6387,8 @@ 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/ath10k-ct/patches/999-800-ath10k-Add-threaded-napi.patch b/package/kernel/ath10k-ct/patches/999-800-ath10k-Add-threaded-napi.patch new file mode 100644 index 00000000000000..e2eff29bb459cd --- /dev/null +++ b/package/kernel/ath10k-ct/patches/999-800-ath10k-Add-threaded-napi.patch @@ -0,0 +1,33 @@ +--- a/ath10k-6.10/pci.c ++++ b/ath10k-6.10/pci.c +@@ -2285,6 +2285,7 @@ static int ath10k_pci_hif_start(struct a + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); + ++ dev_set_threaded(ar->napi_dev, true); + ath10k_core_napi_enable(ar); + + ath10k_pci_irq_enable(ar); +--- a/ath10k-6.10/sdio.c ++++ b/ath10k-6.10/sdio.c +@@ -1863,6 +1863,7 @@ static int ath10k_sdio_hif_start(struct + struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + int ret; + ++ dev_set_threaded(&ar->napi_dev, true); + ath10k_core_napi_enable(ar); + + /* Sleep 20 ms before HIF interrupts are disabled. +--- a/ath10k-6.10/core.c ++++ b/ath10k-6.10/core.c +@@ -4576,6 +4576,10 @@ struct ath10k *ath10k_core_create(size_t + if (!ar->napi_dev) + goto err_free_tx_complete; + ++ snprintf(ar->napi_dev->name, sizeof(ar->napi_dev->name), "ath10k_%s", ++ wiphy_name(ar->hw->wiphy)); ++ dev_set_threaded(ar->napi_dev, true); ++ + ret = ath10k_coredump_create(ar); + if (ret) + goto err_free_netdev; 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/modules/crypto.mk b/package/kernel/linux/modules/crypto.mk index ce221635470302..50a885408f1d9d 100644 --- a/package/kernel/linux/modules/crypto.mk +++ b/package/kernel/linux/modules/crypto.mk @@ -1164,7 +1164,6 @@ endef $(eval $(call KernelPackage,crypto-xts)) - define KernelPackage/crypto-xxhash TITLE:=xxHash non-cryptographic hash algorithm DEPENDS:=+kmod-crypto-hash +kmod-lib-xxhash @@ -1176,3 +1175,20 @@ endef $(eval $(call KernelPackage,crypto-xxhash)) +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 cf66bd8cd8c7ee..c7d89227d4b765 100644 --- a/package/kernel/linux/modules/netfilter.mk +++ b/package/kernel/linux/modules/netfilter.mk @@ -328,6 +328,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 22cd0e97b41770..67cfdff20f8414 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -36,14 +36,21 @@ 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 +IPQ_BRANCH:=$(shell git -C $(TOPDIR) rev-parse --abbrev-ref HEAD) +IPQ_DATE:=$(shell git -C $(TOPDIR) log -1 --format=%cd --date=format:%Y-%m-%d) +IPQ_COMMIT:=$(shell git -C $(TOPDIR) log -1 --format=%h) + define KernelPackage/mac80211/Default SUBMENU:=$(WMENU) URL:=https://wireless.wiki.kernel.org/ @@ -129,10 +136,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 +152,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 +308,16 @@ 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 + +ifeq ($(CONFIG_NSS_FIRMWARE_VERSION_11_4),y) + C_DEFINES+=-DCONFIG_NSS_FIRMWARE_VERSION_11_4 +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)" \ @@ -354,6 +391,11 @@ define Build/Patch $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/brcm,brcm/) $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rtl,rtl/) +ifdef CONFIG_ATH11K_NSS_SUPPORT + $(foreach driver,$(NSS_PATCH), + $(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/nss/$(driver),nss/$(driver)/) + ) +endif $(if $(QUILT),touch $(PKG_BUILD_DIR)/.quilt_used) endef @@ -371,6 +413,11 @@ define Quilt/Refresh/Package $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/brcm,brcm/) $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rtl,rtl/) +ifdef CONFIG_ATH11K_NSS_SUPPORT + $(foreach driver,$(NSS_PATCH), + $(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/nss/$(driver),nss/$(driver)/) + ) +endif endef define Build/Compile @@ -392,6 +439,18 @@ 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 $(1)/usr/bin $(1)/etc/hotplug.d/net + $(INSTALL_BIN) ./files/qca-nss-pbuf.init $(1)/etc/init.d/qca-nss-pbuf + $(INSTALL_DATA) ./files/pbuf.uci $(1)/etc/config/pbuf + $(INSTALL_BIN) ./files/nss_diag.sh $(1)/usr/bin/nss_diag + $(INSTALL_DATA) ./files/disable_mesh_chksum_ath11k.hotplug $(1)/etc/hotplug.d/net/99-disable_mesh_chksum_ath11k + echo "IPQ_BRANCH=$(IPQ_BRANCH)" > $(1)/etc/ipq_release + echo "IPQ_DATE=$(IPQ_DATE)" >> $(1)/etc/ipq_release + echo "IPQ_COMMIT=$(IPQ_COMMIT)" >> $(1)/etc/ipq_release +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 5b13b6aac4ad99..44e95b953a43b3 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 += \ @@ -59,6 +66,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 @@ -309,9 +323,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-qcom-qmi-helpers - FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k.ko + +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 @@ -319,6 +341,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 @@ -326,6 +352,69 @@ 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 + select PACKAGE_kmod-qca-nss-ecm + default y + help + Say Y to enable NSS WiFi offload support. Ensure you enable feeds for NSS drivers. + https://github.com/qosmio/nss-packages + + config ATH11K_NSS_MESH_SUPPORT + bool "Enable NSS WiFi Mesh offload" + depends on ATH11K_NSS_SUPPORT + select PACKAGE_MAC80211_MESH + select NSS_FIRMWARE_VERSION_11_4 + 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/disable_mesh_chksum_ath11k.hotplug b/package/kernel/mac80211/files/disable_mesh_chksum_ath11k.hotplug new file mode 100644 index 00000000000000..a118e7b55c1b53 --- /dev/null +++ b/package/kernel/mac80211/files/disable_mesh_chksum_ath11k.hotplug @@ -0,0 +1,24 @@ +#!/bin/sh +# shellcheck disable=1090 +PROG=${0##*/} +[ "$ACTION" = "add" ] && [ "$DEVTYPE" = "wlan" ] || exit 0 + +UEVENT="/sys/class/net/$INTERFACE/device/uevent" +[ -r "$UEVENT" ] || exit 0 + +DRIVER=$(awk -F= '/^DRIVER/{print $2}' "$UEVENT") +case "$DRIVER" in + *ath11k*) + mode=$(ubus call network.wireless status \ + | jsonfilter -e "@[*].interfaces[@.ifname='$INTERFACE'].config.mode" 2> /dev/null) + [ "$mode" = "mesh" ] || exit 0 + + INC="/lib/netifd/offload/disable_offloads.sh" + . "$INC" 2> /dev/null || { + logger -t "$PROG" -p user.error "missing include script $INC. exiting..." + exit 1 + } + + disable_feature tx-checksumming "$INTERFACE" + ;; +esac diff --git a/package/kernel/mac80211/files/nss_diag.sh b/package/kernel/mac80211/files/nss_diag.sh new file mode 100644 index 00000000000000..34ee50f5ab0726 --- /dev/null +++ b/package/kernel/mac80211/files/nss_diag.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# shellcheck disable=3037,3060,2034,1091,2166 + +# check if stdout is a terminal, then set colors. +if [ -t 1 ]; then + red="\033[31m" + green="\033[32m" + yellow="\033[33m" + blue="\033[34m" + magenta="\033[35m" + cyan="\033[36m" + white="\033[37m" + reset="\033[m" + bold="\033[1m" +fi + +# Retrieve OpenWRT version +[ -r /etc/openwrt_version ] && openwrt_rev=$(cat /etc/openwrt_version) + +# Retrieve device model +model=$(jsonfilter -e ''@.model.name'' < /etc/board.json | sed -e "s/,/_/g") + +# NSS firmware version +nss_fw="/lib/firmware/qca*.bin" +# shellcheck disable=2086 +[ "$(ls $nss_fw 2> /dev/null)" ] && nss_version=$(grep -h -m 1 -a -o 'Version:.[^[:cntrl:]]*' $nss_fw | head -1 | cut -d ' ' -f 2) + +# ATH11K firmware version +ath11k_fw=$(grep -hm1 -a -o 'WLAN.[^[:cntrl:]]*SILICONZ-1' /lib/firmware/*/q6* | head -1) + +# MAC80211 (backports) version +mac80211_version=$(awk '/version/{print $NF;exit}' /lib/modules/*/compat.ko) + +# OpenWRT IPQ release details +[ -r /etc/ipq_release ] && . /etc/ipq_release +ipq_branch=${IPQ_BRANCH:-"N/A"} +ipq_commit=${IPQ_COMMIT:-"N/A"} +ipq_date=${IPQ_DATE:-"N/A"} + +# Defaults for empty variables +openwrt_rev=${openwrt_rev:-"N/A"} +model=${model:-"N/A"} +nss_version=${nss_version:-"N/A"} +ath11k_fw=${ath11k_fw:-"N/A"} +mac80211_version=${mac80211_version:-"N/A"} + +# Display the information +echo -e "${bold}${red} MODEL${reset}: ${blue}${bold}${model}${reset}" +echo -e "${bold}${red} OPENWRT${reset}: ${white}${openwrt_rev}${reset}" +echo -e "${bold}${red}IPQ BRANCH${reset}: ${cyan}${ipq_branch}${reset}" +echo -e "${bold}${red}IPQ COMMIT${reset}: ${cyan}${ipq_commit}${reset}" +echo -e "${bold}${red} IPQ DATE${reset}: ${cyan}${ipq_date}${reset}" +echo -e "${bold}${red} NSS FW${reset}: ${magenta}${nss_version}${reset}" +echo -e "${bold}${red} MAC80211${reset}: ${yellow}${mac80211_version}${reset}" +echo -e "${bold}${red} ATH11K FW${reset}: ${green}${ath11k_fw}${reset}" + +# Display GRO Fragmentation status using BusyBox +echo -ne "${bold}${red} INTERFACE${reset}: ${white}" +n=0 +for iface in /sys/class/net/br-lan/device /sys/class/net/*/device; do + iface=${iface%/*} + iface=${iface##*/} + ethtool -k "$iface" | awk -v n=$n -v i="$iface" -v rst="${reset}" -v red="${red}" -v green="${green}" ' + BEGIN { settings=""; if(n>0) spacing=" " } + /tx-checksumming|rx-gro-list/ { + color=green + if($2=="off") color=red + settings = settings $1 " " sprintf("%s%-3s%s", color,$2,rst) " "; + } + END { printf "%s%-11s%s\n", spacing, i, settings; }' + n=$((n + 1)) +done + +echo -e "${reset}" +echo -ne "${bold}${red} NSS PKGS${reset}: ${white}" + +if cmd=$(command -v apk) > /dev/null; then + flags="list -I" +else + cmd=$(command -v opkg) + flags="list-installed" +fi + +if [ -z "$cmd" ]; then + echo -e "${red}No package manager found${reset}" + exit 1 +fi + +$cmd $flags | awk -v count=0 ' + /kmod-qca|^nss/ { + if(count>0) tab=" " + print tab $0 + count++ +}' +echo -ne "${reset}" diff --git a/package/kernel/mac80211/files/pbuf.uci b/package/kernel/mac80211/files/pbuf.uci new file mode 100644 index 00000000000000..ffbbc8cafd4dfb --- /dev/null +++ b/package/kernel/mac80211/files/pbuf.uci @@ -0,0 +1,8 @@ +config general opt + option auto_scale 'off' + # option memory_profile 'off' + option memory_profile 'auto' + # option memory_profile '1gb' + # option memory_profile '512mb' + # option memory_profile '256mb' + option scaling_governor 'performance' diff --git a/package/kernel/mac80211/files/qca-nss-pbuf.init b/package/kernel/mac80211/files/qca-nss-pbuf.init new file mode 100644 index 00000000000000..bca42e943b3f2f --- /dev/null +++ b/package/kernel/mac80211/files/qca-nss-pbuf.init @@ -0,0 +1,182 @@ +#!/bin/sh /etc/rc.common +# shellcheck disable=3043,3010 +# 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=95 +NAME=qca-nss-pbuf + +reload_wifi() { + if [ -r /sys/module/ath11k/parameters/nss_offload ]; then + nss_offload=$(cat /sys/module/ath11k/parameters/nss_offload 2> /dev/null) + [ "$nss_offload" -eq 1 ] && wifi up + fi +} + +get_num_cpus() { + local num_cpus + num_cpus=$(awk -F': ' '/^processor/ {count++} END {print count}' /proc/cpuinfo) + echo "${num_cpus:-1}" +} + +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 $NAME "$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>&1 + else + logger -t $NAME "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>&1 + + logger -t $NAME "$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" > /dev/null 2>&1 + + logger -t $NAME "$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" > /dev/null 2>&1 + +} + +apply_nss_config() { + local auto_scale n2h_queue_limit_core0 n2h_queue_limit_core1 num_cpus + local hash_bitmap + + if [ ! -r /sys/module/ath11k/parameters/nss_offload ]; then + logger -t $NAME "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 $NAME -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 $NAME -p user.error "NSS driver not loaded or disabled! Exiting... " + exit 1 + } + + config_load pbuf + config_get_bool auto_scale opt auto_scale 0 + config_get n2h_queue_limit_core0 opt n2h_queue_limit_core0 256 + config_get n2h_queue_limit_core1 opt n2h_queue_limit_core1 256 + + sysctl -w dev.nss.clock.auto_scale="$auto_scale" > /dev/null 2>&1 + + sysctl -w dev.nss.n2hcfg.n2h_queue_limit_core0="$n2h_queue_limit_core0" > /dev/null 2>&1 + sysctl -w dev.nss.n2hcfg.n2h_queue_limit_core1="$n2h_queue_limit_core1" > /dev/null 2>&1 + + local memory_profile memtotal board + board=$(board_name) + + if memory_profile=$(uci_get pbuf.opt.memory_profile); then + case "$memory_profile" in + 1024 | 1g* | 512 | 512m* | 256 | 256m*) + logger -t $NAME "Using custom memory profile - $board" + ;; + off* | false* | disable* | 0) + logger -s -t $NAME -p user.warn "NSS pbuf option 'memory_profile=off'. Not running. Enable if you have issues connecting more than 65 clients" + exit 0 + ;; + auto) + memtotal=$(awk '/MemTotal/{print $2}' /proc/meminfo) + [ "$memtotal" -gt 512000 ] && memory_profile=1024 + [ "$memtotal" -le 512000 ] && memory_profile=512 + [ "$memtotal" -le 256000 ] && memory_profile=256 + logger -t $NAME "Setting n2hcfg values for board: $board" + ;; + *) + logger -s -t $NAME -p user.error "Unknown profile $memory_profile. Choose auto, 1gb, 512mb, or 256mb" + exit 1 + ;; + esac + else + exit 0 + fi + + case "$memory_profile" in + # 1GB+ profile + 1024 | 1g*) + extra_pbuf_core0=10000000 n2h_high_water_core0=72512 n2h_wifi_pool_buf=36864 apply_sysctl + ;; + # 512MB profile + 512 | 512m*) + extra_pbuf_core0=3100000 n2h_high_water_core0=30624 n2h_wifi_pool_buf=8192 apply_sysctl + ;; + # 256MB profile + 256 | 256m*) + extra_pbuf_core0=3100000 n2h_high_water_core0=30258 n2h_wifi_pool_buf=4096 apply_sysctl + ;; + esac +} + +set_stats_disable() { + + local stats_disable + config_load pbuf + config_get stats_disable opt stats_disable 1 + + find /sys/kernel/debug/ath11k -name stats_disable | while read -r stats_file; do + stats_msg_prefix="Disabling" + [ "$stats_disable" -eq 0 ] && stats_msg_prefix="Enabling" + logger -t $NAME -p user.notice "$stats_msg_prefix ath11k stats" + echo "$stats_disable" > "$stats_file" + done +} + +set_scaling_governor() { + + local scaling_governor num_cpus + config_load pbuf + config_get scaling_governor opt scaling_governor + + scaling_available_governors=/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors + + if [ -r "$scaling_available_governors" ]; then + scaling_available_governors=$(cat $scaling_available_governors) + else + return 0 + fi + + if [ -n "$scaling_available_governors" ] && [ -n "$scaling_governor" ]; then + if [[ "$scaling_available_governors" =~ $scaling_governor ]]; then + logger -t $NAME -p user.notice "Setting CPU scaling governor to '$scaling_governor'" + num_cpus=$(get_num_cpus) + for num in $(seq 0 $((num_cpus - 1))); do + echo "$scaling_governor" > "/sys/devices/system/cpu/cpu${num}/cpufreq/scaling_governor" + done + else + logger -t $NAME -p user.error "'$scaling_governor' not available in CPU governors [ $scaling_available_governors ]" + fi + fi +} + +start() { + + set_scaling_governor + set_stats_disable + apply_nss_config + + num_cpus=$(get_num_cpus) + hash_bitmap="$(((1 << num_cpus) - 1))" + sysctl -w dev.nss.rps.hash_bitmap=$hash_bitmap > /dev/null 2>&1 + reload_wifi + +} diff --git a/package/kernel/mac80211/patches/nss/ath10k/100-ath10k-move-peer-unmap-warning-to-debug.patch b/package/kernel/mac80211/patches/nss/ath10k/100-ath10k-move-peer-unmap-warning-to-debug.patch new file mode 100644 index 00000000000000..f09519e4c73b61 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath10k/100-ath10k-move-peer-unmap-warning-to-debug.patch @@ -0,0 +1,20 @@ +Remove not needed warning when get peer unmap +event from the firmware. This is not critical +message. Instead print this as a debug message. + +Signed-off-by: Janusz Dziedzic +--- + drivers/net/wireless/ath/ath10k/txrx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath10k/txrx.c ++++ b/drivers/net/wireless/ath/ath10k/txrx.c +@@ -266,7 +266,7 @@ void ath10k_peer_unmap_event(struct ath1 + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, ev->peer_id); + if (!peer) { +- ath10k_warn(ar, "peer-unmap-event: unknown peer id %d\n", ++ ath10k_dbg(ar, ATH10K_DBG_HTT, "peer-unmap-event: unknown peer id %d\n", + ev->peer_id); + goto exit; + } 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..5e8c5f08ad99ee --- /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 +@@ -5527,7 +5527,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; +@@ -5549,6 +5549,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/ath10k/999-800-ath10k-Add-threaded-napi.patch b/package/kernel/mac80211/patches/nss/ath10k/999-800-ath10k-Add-threaded-napi.patch new file mode 100644 index 00000000000000..c97fd769193751 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath10k/999-800-ath10k-Add-threaded-napi.patch @@ -0,0 +1,43 @@ +--- a/drivers/net/wireless/ath/ath10k/pci.c ++++ b/drivers/net/wireless/ath/ath10k/pci.c +@@ -1975,6 +1975,7 @@ static int ath10k_pci_hif_start(struct a + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n"); + ++ dev_set_threaded(ar->napi_dev, true); + ath10k_core_napi_enable(ar); + + ath10k_pci_irq_enable(ar); +--- a/drivers/net/wireless/ath/ath10k/sdio.c ++++ b/drivers/net/wireless/ath/ath10k/sdio.c +@@ -1863,6 +1863,7 @@ static int ath10k_sdio_hif_start(struct + struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar); + int ret; + ++ dev_set_threaded(&ar->napi_dev, true); + ath10k_core_napi_enable(ar); + + /* Sleep 20 ms before HIF interrupts are disabled. +--- a/drivers/net/wireless/ath/ath10k/core.c ++++ b/drivers/net/wireless/ath/ath10k/core.c +@@ -3707,6 +3707,10 @@ struct ath10k *ath10k_core_create(size_t + if (!ar->napi_dev) + goto err_free_tx_complete; + ++ snprintf(ar->napi_dev->name, sizeof(ar->napi_dev->name), "ath10k_%s", ++ wiphy_name(ar->hw->wiphy)); ++ dev_set_threaded(ar->napi_dev, true); ++ + ret = ath10k_coredump_create(ar); + if (ret) + goto err_free_netdev; +--- a/drivers/net/wireless/ath/ath10k/ahb.c ++++ b/drivers/net/wireless/ath/ath10k/ahb.c +@@ -622,6 +622,7 @@ static int ath10k_ahb_hif_start(struct a + ath10k_core_napi_enable(ar); + ath10k_ce_enable_interrupts(ar); + ath10k_pci_enable_intx_irq(ar); ++ dev_set_threaded(&ar->napi_dev, true); + + ath10k_pci_rx_post(ar); + 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..6764ffbcebc26d --- /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 +@@ -4275,22 +4275,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; +@@ -4298,10 +4304,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..3b21ce04e7c083 --- /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 +@@ -44,6 +44,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 +@@ -419,6 +421,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; +@@ -430,10 +443,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]; +@@ -441,6 +450,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 +@@ -11,6 +11,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, +@@ -248,8 +249,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; +@@ -280,14 +287,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, +@@ -302,14 +301,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], +@@ -848,6 +929,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) + { +@@ -856,9 +971,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 +@@ -42,6 +42,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 +@@ -976,44 +976,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; + } +@@ -1471,6 +1505,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 +@@ -20,7 +20,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; +@@ -104,6 +108,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 +@@ -128,6 +148,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 +@@ -270,6 +270,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..d1773499512d35 --- /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 +@@ -31,6 +31,7 @@ + #include "dbring.h" + #include "spectral.h" + #include "wow.h" ++#include "rx_desc.h" + #include "fw.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) +@@ -512,6 +513,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 { +@@ -519,6 +522,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 { +@@ -635,11 +641,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 +@@ -14,13 +14,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; +@@ -65,6 +91,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; + +@@ -125,6 +188,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, +@@ -141,12 +215,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; +@@ -164,45 +239,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, +@@ -211,10 +287,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); +@@ -222,6 +356,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 +@@ -1253,9 +1253,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; + +@@ -1268,6 +1269,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)) { +@@ -1300,6 +1321,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; +@@ -1328,44 +1350,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; + } + +@@ -1383,8 +1391,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; +@@ -1415,6 +1423,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. +@@ -1509,6 +1522,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); +@@ -1561,13 +1580,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; + +@@ -1602,6 +1697,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 +@@ -1495,6 +1495,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..6e50b3d99b9696 --- /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 +@@ -28,10 +28,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..0de45a755239a3 --- /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 +@@ -526,13 +526,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 +@@ -1479,6 +1479,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 +@@ -1488,6 +1489,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 +@@ -415,6 +415,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..2a98923fa46254 --- /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 +@@ -5515,8 +5515,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 +@@ -901,6 +901,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 +@@ -71,6 +71,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, +@@ -172,6 +173,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..ce57ea9aed2ec6 --- /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 +@@ -525,6 +525,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); ++ } + + } + +@@ -5444,6 +5560,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) + { +@@ -5517,8 +5682,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 +@@ -805,7 +805,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) +@@ -837,6 +836,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 = +@@ -860,6 +861,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); + } +@@ -889,6 +893,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: { +@@ -943,15 +955,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; +@@ -959,12 +972,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 +@@ -73,6 +73,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, +@@ -83,14 +87,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; +@@ -104,7 +109,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; + }; +@@ -145,6 +150,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; +@@ -213,9 +219,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..7d33113a097632 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/142-ath11k-adding-support-for-mgmt-frame-stats.patch @@ -0,0 +1,307 @@ +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -17,6 +17,7 @@ + #include + #include + ++#include "fw.h" + #include "qmi.h" + #include "htc.h" + #include "wmi.h" +@@ -32,7 +33,6 @@ + #include "spectral.h" + #include "wow.h" + #include "rx_desc.h" +-#include "fw.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -358,6 +358,16 @@ struct ath11k_reg_tpc_power_info { + struct ath11k_chan_power_info chan_power_info[ATH11K_NUM_PWR_LEVELS]; + }; + ++#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; +@@ -415,6 +425,7 @@ struct ath11k_vif { + struct ath11k_rekey_data rekey_data; + + struct ath11k_reg_tpc_power_info reg_tpc_info; ++ 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 +@@ -1589,6 +1589,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; +@@ -1621,6 +1702,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 +@@ -6031,9 +6031,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) { +@@ -6059,9 +6059,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)); +@@ -6075,12 +6077,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 +@@ -5941,6 +5941,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); +@@ -5968,6 +5974,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); +@@ -7547,6 +7578,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"); +@@ -7612,7 +7648,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..05055586ccea73 --- /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 +@@ -2303,5 +2303,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 +@@ -17,6 +17,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", +@@ -992,8 +994,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) +@@ -1046,6 +1046,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", +@@ -1676,6 +1694,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); +@@ -1753,6 +1774,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 +@@ -264,6 +264,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); +@@ -315,6 +317,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..8bff0bf7766c92 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/188-ath11k-m3-ssr-dump-collection.patch @@ -0,0 +1,11 @@ +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2100,6 +2100,8 @@ 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: ++ 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..0d1de3b2f45b08 --- /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 +@@ -10211,6 +10211,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 +@@ -4173,6 +4173,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..4bb181536a3bc0 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/199-002-ath11k_nss-add-nss-driver-interface.patch @@ -0,0 +1,3036 @@ +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 +@@ -27,6 +27,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,2399 @@ ++// 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 (peer_id: %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_dbg(ar->ab, ATH11K_DBG_DP_TX, "encap mismatch in nss tx skb encap type %d" \ ++ " vif encap type %d\n", encap_type, arvif->nss.encap); ++ ath11k_dbg_dump(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX), "", "nss tx msdu: ", ++ skb->data, skb->len); ++ 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); ++ if(ar->ab->nss.debug_mode) ++ 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 +@@ -31,6 +31,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) +@@ -137,6 +138,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 +@@ -70,6 +70,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; +@@ -723,6 +737,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 +@@ -993,6 +993,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 +@@ -1169,6 +1169,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 +@@ -2890,6 +2890,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; +@@ -3138,6 +3159,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; +@@ -6390,6 +6421,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 +@@ -21,6 +21,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, +@@ -76,6 +104,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 +@@ -6250,6 +6250,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 +@@ -891,6 +891,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 { +@@ -977,6 +979,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..63148a4a922143 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/199-003-ath11k-add-nss-support.patch @@ -0,0 +1,1209 @@ +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 +@@ -527,6 +527,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]; +@@ -543,20 +549,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)) +@@ -569,7 +575,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); +@@ -910,6 +916,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 +@@ -18,6 +18,12 @@ + #include "wow.h" + #include "fw.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); +@@ -1636,10 +1642,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); +@@ -1661,6 +1673,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: +@@ -1674,6 +1688,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); +@@ -1880,6 +1898,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_dp_pdev_free(ab); + ath11k_spectral_deinit(ab); +@@ -2208,6 +2230,10 @@ int ath11k_core_pre_init(struct ath11k_b + ath11k_err(ab, "failed to pre init firmware: %d", 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 +@@ -33,6 +33,7 @@ + #include "spectral.h" + #include "wow.h" + #include "rx_desc.h" ++#include "nss.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -426,6 +427,9 @@ struct ath11k_vif { + + struct ath11k_reg_tpc_power_info reg_tpc_info; + struct ath11k_mgmt_frame_stats mgmt_stats; ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ struct arvif_nss nss; ++#endif + }; + + struct ath11k_vif_iter { +@@ -579,6 +583,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 */ +@@ -673,6 +680,9 @@ struct ath11k { + struct ath11k_pdev *pdev; + struct ieee80211_hw *hw; + 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; +@@ -934,9 +944,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", +@@ -135,6 +140,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) { +@@ -792,14 +809,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; +@@ -807,7 +826,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); +@@ -818,7 +837,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); +@@ -852,7 +871,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_rxdma_per_pdev; j++) { + int id = i * ab->hw_params.num_rxdma_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 +@@ -18,6 +18,7 @@ + #include "hal_rx.h" + #include "dp_tx.h" + #include "peer.h" ++#include "nss.h" + + #define ATH11K_DP_RX_FRAGMENT_TIMEOUT_MS (2 * HZ) + +@@ -214,8 +215,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); + } +@@ -226,8 +227,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); + } +@@ -284,7 +285,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); +@@ -499,7 +500,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; +@@ -2062,7 +2065,7 @@ int ath11k_dp_rx_crypto_mic_len(struct a + 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) + { +@@ -3125,7 +3144,8 @@ static void ath11k_dp_rx_update_user_sta + 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", ++ if (!ar->ab->nss.enabled) ++ ath11k_warn(ar->ab, "peer ast idx %d can't be found\n", + user_stats->ast_index); + return; + } +@@ -3133,6 +3153,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 +3236,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) +@@ -5677,7 +5706,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; +@@ -5745,6 +5774,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 +@@ -148,4 +148,16 @@ int ath11k_dp_rx_pktlog_stop(struct ath1 + + 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 +@@ -939,6 +939,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, \ +@@ -1680,6 +1681,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", +@@ -1719,6 +1725,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); + } + +@@ -3157,6 +3169,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); +@@ -3199,6 +3217,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); + +@@ -3491,6 +3513,28 @@ static bool ath11k_mac_supports_station_ + chandef->chan->band == NL80211_BAND_6GHZ; + } + ++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, +@@ -4392,6 +4436,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) { +@@ -4430,9 +4494,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; +@@ -6099,10 +6162,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; + } + } + +@@ -6124,6 +6191,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); + } +@@ -6422,7 +6491,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; +@@ -6468,6 +6537,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) +@@ -6598,6 +6669,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", +@@ -6751,7 +6824,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, +@@ -6881,6 +6981,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); +@@ -7399,6 +7500,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 */ +@@ -9026,6 +9131,8 @@ static void ath11k_mac_op_sta_statistics + sinfo->signal_avg += 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) +@@ -9723,6 +9830,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, +@@ -10161,7 +10269,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; +@@ -10276,6 +10385,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 +@@ -583,6 +583,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 +@@ -11,11 +11,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; + +@@ -666,6 +665,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; +@@ -1688,6 +1688,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; +@@ -1754,6 +1824,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 +@@ -39,6 +39,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)) +@@ -661,12 +663,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; +@@ -679,6 +683,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 + */ +@@ -1140,7 +1160,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; + +@@ -1234,6 +1253,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; + +@@ -1293,7 +1315,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 +@@ -589,6 +589,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 +@@ -165,6 +165,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..7c3b2cc605c0fc --- /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 +@@ -149,6 +149,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] = { +@@ -8807,6 +8809,74 @@ out: + 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, 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; +@@ -8925,6 +8995,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 +@@ -772,6 +772,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, +@@ -1884,6 +1885,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, +@@ -4896,6 +4898,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..1b9cae352afd66 --- /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 +@@ -122,6 +122,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 +@@ -17,6 +17,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 +@@ -10402,6 +10402,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..37ef93a5574c7b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/207-ath11k-Enable-256_512MB-profiles.patch @@ -0,0 +1,354 @@ +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; + bool support_dual_stations; + }; + +--- 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 +@@ -166,6 +166,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 +@@ -17,7 +17,6 @@ + #include + #include + +-#include "fw.h" + #include "qmi.h" + #include "htc.h" + #include "wmi.h" +@@ -34,6 +33,7 @@ + #include "wow.h" + #include "rx_desc.h" + #include "nss.h" ++#include "fw.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) + +@@ -929,6 +929,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; +@@ -1086,6 +1091,8 @@ struct ath11k_base { + DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT); + } fw; + ++ 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); + +@@ -833,6 +836,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 +@@ -43,6 +43,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, +@@ -128,6 +130,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, + .support_dual_stations = false, + }, + { +@@ -179,7 +182,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, +@@ -265,7 +268,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 = 2 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -437,7 +440,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 = 2 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -473,6 +476,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, + .support_dual_stations = true, + }, + { +@@ -524,7 +528,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 = 2 + 1, + .num_peers = 512, + .supports_suspend = true, +@@ -560,6 +564,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, + .support_dual_stations = true, + }, + { +@@ -609,7 +614,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 = 3, + .num_peers = 512, + .supports_suspend = false, +@@ -689,7 +694,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, +@@ -724,6 +729,7 @@ 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, + .support_dual_stations = false, + }, + { +@@ -817,6 +823,21 @@ static struct ath11k_hw_params ath11k_hw + }, + }; + ++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, ++ }, ++}; ++ + static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) + { + WARN_ON(!ab->hw_params.single_pdev_only); 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..4d06a032a78d18 --- /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; + } +@@ -1324,7 +1549,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; +@@ -1338,9 +1563,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; + } +@@ -1413,8 +1639,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; +@@ -1471,17 +1698,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; +@@ -1500,6 +1733,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) +@@ -1893,7 +2319,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; + } + +@@ -1947,7 +2373,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..70d217b8c73650 --- /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 +@@ -683,6 +683,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]; +@@ -1101,6 +1102,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 +@@ -1858,6 +1858,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); + +@@ -1893,15 +1895,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 +@@ -499,7 +499,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 +@@ -161,6 +161,8 @@ static const struct wmi_tlv_policy wmi_t + .min_len = sizeof(struct ath11k_wmi_p2p_noa_info) }, + [WMI_TAG_P2P_NOA_EVENT] = { + .min_len = sizeof(struct wmi_p2p_noa_event) }, ++ [WMI_TAG_WDS_ADDR_EVENT] = { ++ .min_len = sizeof(struct wmi_wds_addr_event) }, + }; + + #define PRIMAP(_hw_mode_) \ +@@ -1131,6 +1133,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) + { +@@ -6481,6 +6528,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, 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) + { +@@ -7305,6 +7382,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: +@@ -8877,6 +8955,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; +@@ -9010,6 +9104,9 @@ static void ath11k_wmi_tlv_op_rx(struct + case WMI_P2P_NOA_EVENTID: + ath11k_wmi_p2p_noa_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 +@@ -3028,6 +3028,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; +@@ -4641,6 +4656,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 + */ +@@ -6440,6 +6470,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..80bbf23a58b04d --- /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 +@@ -6012,6 +6012,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); +@@ -6079,8 +6089,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; + +@@ -6142,7 +6152,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", +@@ -6157,6 +6167,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 +@@ -118,7 +118,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] +@@ -706,6 +706,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) + { +@@ -6023,8 +6072,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; +@@ -6062,6 +6111,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); + +@@ -6080,10 +6134,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: +@@ -6105,12 +6162,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, GFP_ATOMIC); +@@ -6120,7 +6178,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); +@@ -7838,10 +7896,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; + } +@@ -7854,7 +7913,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", +@@ -7865,6 +7924,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) +@@ -9107,6 +9196,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 +@@ -376,6 +376,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, +@@ -706,6 +707,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, +@@ -1895,6 +1898,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_VDEV_SET_TPC_POWER_CMD = 0x3B5, + WMI_TAG_VDEV_CH_POWER_INFO, + WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, +@@ -2126,7 +2132,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, +@@ -3883,6 +3899,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) +@@ -3893,9 +3910,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; +@@ -4987,7 +5005,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; +@@ -5819,6 +5837,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 MAX_RADIOS 3 + + #define WMI_SERVICE_READY_TIMEOUT_HZ (5 * HZ) +@@ -6428,6 +6457,8 @@ int ath11k_wmi_mgmt_send(struct ath11k * + struct sk_buff *frame); + int ath11k_wmi_p2p_go_bcn_ie(struct ath11k *ar, u32 vdev_id, + const u8 *p2p_ie); ++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..8eed908cfc7e1f --- /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; +@@ -869,10 +942,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), +@@ -1214,6 +1293,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; + +@@ -1245,6 +1325,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; +@@ -1254,6 +1340,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; + +@@ -1281,11 +1368,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, +@@ -1379,22 +1817,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; + } +@@ -1466,13 +1904,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) +@@ -1776,8 +2214,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); +@@ -1822,8 +2260,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..0be8370d4eb04a --- /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,573 @@ +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 +@@ -431,6 +431,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 +@@ -4844,6 +4844,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; + +@@ -5013,17 +5018,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, +@@ -5031,8 +5047,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, +@@ -5134,9 +5235,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; + } +@@ -6524,6 +6648,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 && +@@ -6754,7 +6881,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; +@@ -6767,6 +6895,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_WORK(&arvif->bcn_tx_work, ath11k_mac_bcn_tx_work); + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath11k_mac_vif_sta_connection_loss_work); +@@ -6799,6 +6949,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); + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; + break; +@@ -7034,14 +7185,31 @@ 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; + ++ 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); + cancel_work_sync(&arvif->bcn_tx_work); + +- mutex_lock(&ar->conf_mutex); +- + ath11k_dbg(ab, ATH11K_DBG_MAC, "remove interface (vdev %d)\n", + arvif->vdev_id); + +@@ -7058,6 +7226,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); +@@ -7099,8 +7275,7 @@ err_vdev_del: + /* Recalc txpower for remaining vdev */ + ath11k_mac_txpower_recalc(ar); + +- /* TODO: recal traffic pause state based on the available vdevs */ +- ++unlock: + mutex_unlock(&ar->conf_mutex); + } + +@@ -7160,16 +7335,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: +@@ -9097,6 +9273,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); +@@ -9156,7 +9333,8 @@ static void ath11k_mac_op_sta_statistics + + 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 +@@ -5111,6 +5111,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 +@@ -1122,12 +1122,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, +@@ -1139,13 +1140,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 +@@ -89,9 +89,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(rx, 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(); +@@ -1000,6 +1006,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..bc0618693f4f25 --- /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 +@@ -1530,14 +1530,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, +@@ -1565,7 +1562,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); +@@ -1574,14 +1572,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"); +@@ -1590,8 +1593,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; + } +@@ -1620,7 +1623,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; + +@@ -1733,6 +1736,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..360ff2212712b9 --- /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 +@@ -102,6 +102,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 : +@@ -370,6 +375,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; +@@ -432,6 +451,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 +@@ -343,6 +343,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; +@@ -715,6 +719,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; +@@ -4265,6 +4296,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; +@@ -4353,15 +4387,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; +@@ -4379,17 +4438,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. + */ +@@ -4434,6 +4514,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); + +@@ -4456,6 +4604,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) { +@@ -4465,18 +4634,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: +@@ -6899,7 +7073,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; +@@ -6912,6 +7086,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; + } +@@ -6937,6 +7112,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: +@@ -6986,7 +7175,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++; +@@ -7151,7 +7340,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; + } + } + +@@ -7162,6 +7351,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); + +@@ -7260,6 +7451,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, +@@ -9884,6 +10076,33 @@ static int ath11k_mac_station_remove(str + return ret; + } + ++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, +@@ -9973,6 +10192,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); ++ } + } + + if (!ret && +@@ -10587,8 +10834,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 +@@ -1982,6 +1982,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, +@@ -4321,6 +4322,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, +@@ -4543,6 +4545,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 +@@ -3755,6 +3755,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; +@@ -5815,6 +5816,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-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..5316894fd2ef06 --- /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 +@@ -769,6 +769,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; +@@ -777,9 +778,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 +@@ -668,7 +668,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) +@@ -681,8 +682,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 +@@ -946,8 +946,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_next_peek(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..b73f78772b8fda --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/237-006-ath11k-Allow-fast-rx-by-bypassing-stats-update.patch @@ -0,0 +1,442 @@ +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 | 2 + drivers/net/wireless/ath/ath11k/debugfs.c | 75 ++++++++++++++++++ + drivers/net/wireless/ath/ath11k/dp_rx.c | 119 +++++++++++++++++++++++++++--- + drivers/net/wireless/ath/ath11k/hw.c | 22 +++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + drivers/net/wireless/ath/ath11k/mac.c | 8 ++ + 6 files changed, 218 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -146,6 +146,7 @@ struct ath11k_skb_rxcb { + u8 tid; + u16 peer_id; + u16 seq_no; ++ struct napi_struct *napi; + }; + + enum ath11k_hw_rev { +@@ -1130,6 +1131,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 +@@ -974,6 +974,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_rxdma_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)) +@@ -1023,6 +1096,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; + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -341,6 +341,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, +@@ -1665,7 +1671,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; + +@@ -4387,6 +4486,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; +@@ -4436,7 +4536,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 +@@ -261,6 +261,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 +@@ -10246,6 +10246,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..11c07f2f5deafa --- /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 +@@ -118,6 +118,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 { +@@ -925,10 +926,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 +@@ -372,7 +372,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); +@@ -387,7 +387,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); + } +@@ -421,6 +421,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) { +@@ -428,7 +433,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; + +@@ -451,7 +456,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, +@@ -1065,7 +1070,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); +@@ -1116,7 +1121,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 -EOPNOTSUPP; + +- 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; +@@ -636,6 +667,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 */ +@@ -646,6 +678,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; +@@ -704,7 +750,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); +@@ -760,19 +806,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); + +@@ -787,33 +850,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, +@@ -846,7 +903,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 +@@ -6484,12 +6484,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->status_data || 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; + } +@@ -7457,7 +7467,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 +@@ -833,10 +833,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 +@@ -190,7 +190,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, +@@ -216,6 +215,8 @@ static struct ath11k_hw_params ath11k_hw + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, + .support_dual_stations = 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", +@@ -362,7 +363,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, +@@ -388,6 +388,8 @@ static struct ath11k_hw_params ath11k_hw + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, + .support_dual_stations = 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", +@@ -2256,6 +2258,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/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..32ad0a5b549bd2 --- /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 +@@ -29,6 +29,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 +@@ -1410,6 +1410,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) +@@ -1433,6 +1498,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); +@@ -1566,6 +1634,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(); + } +@@ -1686,6 +1756,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) + { +@@ -1704,6 +1837,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) { +@@ -1771,6 +1913,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 +@@ -3563,6 +3563,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); + } + +@@ -10286,6 +10298,28 @@ static int ath11k_mac_op_sta_state(struc + return ret; + } + ++#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, +@@ -10344,6 +10378,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, +@@ -10856,6 +10893,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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ab, ATH11K_DBG_NSS, "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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ab, ATH11K_DBG_NSS_MESH, "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_dbg(ar->ab, ATH11K_DBG_DP_TX, "encap mismatch in nss tx skb encap type %d" \ + " vif encap type %d\n", encap_type, arvif->nss.encap); + ath11k_dbg_dump(ar->ab, (ATH11K_DBG_NSS | ATH11K_DBG_DP_TX), "", "nss tx msdu: ", +@@ -948,16 +2042,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), +@@ -1060,6 +2183,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; + +@@ -1098,6 +2224,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; +@@ -1109,6 +2268,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); +@@ -1116,6 +2283,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) + { +@@ -1143,6 +2382,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); +@@ -1152,6 +2400,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; +@@ -1171,6 +2475,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); +@@ -1178,11 +2487,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); +@@ -1204,6 +2598,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); +@@ -1241,7 +2645,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; + +@@ -1257,6 +2661,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; +@@ -1313,6 +2756,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; +@@ -1360,6 +2812,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; +@@ -2734,6 +4195,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; +@@ -2866,6 +4372,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 +@@ -166,6 +166,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..ea279879b80d4d --- /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 +@@ -2132,6 +2166,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; + } +@@ -2654,12 +2691,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: +@@ -2990,7 +3046,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) +@@ -3004,7 +3059,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..5a3497c1ee0bb2 --- /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 +@@ -7476,8 +7476,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 +@@ -6084,13 +6084,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); + +@@ -6098,6 +6098,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; + } + +@@ -6106,6 +6107,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)) && +@@ -6121,34 +6144,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..770c6dc66456c7 --- /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 +@@ -10,7 +10,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..504d3b959b1c3e --- /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 +@@ -1265,6 +1265,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 +@@ -2325,6 +2325,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); +@@ -2338,6 +2339,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 +@@ -33,6 +33,7 @@ + #include "wow.h" + #include "rx_desc.h" + #include "nss.h" ++#include "peer.h" + #include "fw.h" + + #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) +@@ -1134,6 +1135,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); +@@ -3799,11 +3800,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; +@@ -3926,7 +3923,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; +@@ -3944,8 +3941,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 +@@ -1004,6 +1004,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..000e75f1db6ac2 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/335-0001-ath11k-optimize-tx-completions.patch @@ -0,0 +1,238 @@ +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 +@@ -655,9 +655,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 }; +@@ -667,9 +699,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; + } +@@ -678,11 +712,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); +@@ -692,6 +729,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; +@@ -708,10 +747,10 @@ 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 = ts->ack_rssi; ++ info->status.ack_signal = ts.ack_rssi; + + if (!test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ab->wmi_ab.svc_map)) +@@ -720,40 +759,40 @@ static void ath11k_dp_tx_complete_msdu(s + 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; +@@ -775,37 +814,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; +@@ -829,9 +837,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; +@@ -871,14 +879,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, +@@ -902,7 +912,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..7cff4f768ddc8c --- /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 +@@ -390,6 +390,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); +@@ -402,7 +404,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, +@@ -461,6 +463,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, +@@ -1045,9 +1059,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, +@@ -1061,21 +1074,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", +@@ -878,6 +889,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, +@@ -896,17 +908,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..ddce2ead1ba3ae --- /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 +@@ -10779,6 +10779,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..730d7487429fee --- /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 +@@ -6018,12 +6018,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); +@@ -6347,6 +6358,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; + +@@ -6386,7 +6404,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; +@@ -6417,16 +6434,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/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..dbc2b61dc16b9c --- /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 +@@ -1944,6 +1944,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..31043cf23f8722 --- /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 +@@ -81,6 +81,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/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..efb38b457b1523 --- /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 +@@ -397,8 +397,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..7bf1eadb9431d3 --- /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 +@@ -4991,12 +4991,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; + } + +@@ -10198,6 +10192,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..cc2981a2c0fac4 --- /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 +@@ -3874,13 +3874,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); +@@ -3890,7 +3891,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..f7abe8b21bae83 --- /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 +@@ -1289,7 +1289,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; + +@@ -1370,6 +1370,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, +@@ -1381,6 +1383,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..dcc20f88098fe3 --- /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 +@@ -944,6 +944,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 +@@ -849,6 +849,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 +@@ -1115,8 +1115,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 +@@ -22,6 +22,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) + { +@@ -673,13 +676,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); +@@ -725,14 +765,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) { +@@ -743,11 +787,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); +@@ -755,24 +805,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? */ +@@ -781,6 +828,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; +@@ -798,13 +868,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); +@@ -820,34 +897,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..9f8295e77c56a2 --- /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 +@@ -6593,7 +6593,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..9321d845c6e77e --- /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 +@@ -8700,6 +8700,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..124f62f9242fd5 --- /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 +@@ -10770,6 +10770,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..6f02ed6b50a62a --- /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 +@@ -222,6 +222,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..2b654f7a2a3714 --- /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 +@@ -393,9 +393,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; + +@@ -431,8 +431,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/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..a7968c4e10f387 --- /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 +@@ -81,6 +81,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 +@@ -21,7 +21,9 @@ ath11k-y += core.o \ + fw.o \ + p2p.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 +@@ -1876,7 +1876,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 +@@ -12,7 +12,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) + { +@@ -552,6 +554,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) + { +@@ -623,6 +626,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, +@@ -907,6 +911,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, +@@ -966,6 +971,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, +@@ -1112,8 +1118,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); +@@ -1123,10 +1131,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 +@@ -1724,8 +1724,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; +@@ -2171,7 +2173,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" +@@ -554,7 +556,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; +@@ -643,8 +647,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 +@@ -10376,7 +10376,7 @@ static const struct ieee80211_ops ath11k + .set_wakeup = ath11k_wow_op_set_wakeup, + #endif + +-#ifdef CPTCFG_ATH11K_DEBUGFS ++#ifdef CPTCFG_ATH11K_DEBUGFS_STA + .vif_add_debugfs = ath11k_debugfs_op_vif_add, + .sta_add_debugfs = ath11k_debugfs_sta_op_add, + #endif +--- a/local-symbols ++++ b/local-symbols +@@ -171,6 +171,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..c6550184edcddb --- /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 +@@ -2203,6 +2203,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 +@@ -2251,7 +2252,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; +@@ -2269,7 +2271,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/910-wifi-ath11k-Add-lock-when-accessing-idr_pool-of-tx_r.patch b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-Add-lock-when-accessing-idr_pool-of-tx_r.patch new file mode 100644 index 00000000000000..3bcd37c665dff7 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-Add-lock-when-accessing-idr_pool-of-tx_r.patch @@ -0,0 +1,75 @@ +From 1d840740e28058a5be16d96202c076f552839d7a Mon Sep 17 00:00:00 2001 +From: Aishwarya R +Date: Tue, 19 Mar 2024 15:14:30 +0530 +Subject: [PATCH] wifi: ath11k: Add lock when accessing idr_pool of tx_ring + +Lock is missed while accessing idr_pool of tx_ring which +causes Use after free crash in dp_free path when unloading +the module. + +Fix this by adding tx_idr_lock when accessing idr_pool +of tx_ring. + +Signed-off-by: Aishwarya R +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -347,8 +347,10 @@ fail_remove_idr: + if (ti.pkt_offset) + skb_pull(skb, ti.pkt_offset); + ++ spin_lock_bh(&tx_ring->tx_idr_lock); + tx_ring->idr_pool[idr].id = -1; + clear_bit(idr, tx_ring->idrs); ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (tcl_ring_retry) + goto tcl_ring_sel; +@@ -364,12 +366,14 @@ static void ath11k_dp_tx_free_txbuf(stru + struct sk_buff *msdu = NULL; + struct ath11k_skb_cb *skb_cb; + ++ spin_lock_bh(&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); + } ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", +@@ -401,12 +405,14 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + u32 msdu_id = ts->msdu_id; + u8 flags = 0; + ++ spin_lock_bh(&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); + } ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (unlikely(!msdu)) { + ath11k_warn(ab, "htt tx completion for unknown msdu_id %d\n", +@@ -898,12 +904,14 @@ void ath11k_dp_tx_completion_handler(str + continue; + } + ++ spin_lock_bh(&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); + } ++ spin_unlock_bh(&tx_ring->tx_idr_lock); + + if (unlikely(!msdu)) { + ath11k_warn(ab, "tx completion for unknown msdu_id %d\n", diff --git a/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-correctly-free-skb-using-ieee80211_free_txskb.patch b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-correctly-free-skb-using-ieee80211_free_txskb.patch new file mode 100644 index 00000000000000..27ea07015b82b3 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-correctly-free-skb-using-ieee80211_free_txskb.patch @@ -0,0 +1,36 @@ +From 2ff5eea9f7a5ad18e1ac8f48c29786083d4abaa3 Mon Sep 17 00:00:00 2001 +From: Sarika Sharma +Date: Thu, 14 Mar 2024 08:44:09 +0530 +Subject: [PATCH] wifi: ath11k: correctly free skb using ieee80211_free_txskb() + +While freeing skb in tx completion path for status reinject, inspect +or vdevid mismatch, dev_kfree_skb_any() is used but when a function +is using functions from mac80211 to free an skb then it should do +it consistently and not switch to the generic dev_kfree_skb_any. +Otherwise, mac80211 will not be aware of the freed skb and thus +not clean up related information in its internal data structures. + +Hence fix the issue by properly using ieee80211_free_txskb(). + +Signed-off-by: Sarika Sharma +--- + 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 +@@ -384,9 +384,13 @@ static void ath11k_dp_tx_free_txbuf(stru + skb_cb = ATH11K_SKB_CB(msdu); + + dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); +- dev_kfree_skb_any(msdu); + + ar = ab->pdevs[mac_id].ar; ++ if (ab->stats_disable) ++ dev_kfree_skb_any(msdu); ++ else ++ ieee80211_free_txskb(ar->hw, msdu); ++ + if (atomic_dec_and_test(&ar->dp.num_tx_pending)) + wake_up(&ar->dp.tx_empty_waitq); + } diff --git a/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-remove-call-trace-while-rebooting-AP.patch b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-remove-call-trace-while-rebooting-AP.patch new file mode 100644 index 00000000000000..dd2af91aa0940b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/910-wifi-ath11k-remove-call-trace-while-rebooting-AP.patch @@ -0,0 +1,43 @@ +From 214fcc7e98445c0af0ef75df3174227484da13e2 Mon Sep 17 00:00:00 2001 +From: Sarika Sharma +Date: Wed, 21 Feb 2024 10:23:35 +0530 +Subject: [PATCH] wifi: ath11k: remove call trace while rebooting AP + +When a station idles for a long time, hostapd will try to send +a QoS Null frame to the station as "poll". NL80211_CMD_PROBE_CLIENT +is used for this purpose. +And the skb will be added to ack_status_frame - waiting for a +completion via ieee80211_report_ack_skb(). + +But when the peer was already removed before the tx_complete arrives, +the peer will be missing. And when using dev_kfree_skb_any (instead +of going through mac80211), the entry will stay inside +ack_status_frames thus not clean up related information in its +internal data structures. This IDR will therefore run full after +8K request were generated for such clients. +At this point, the access point will then just stall and not allow +any new clients because idr_alloc() for ack_status_frame will fail. + +ieee80211_free_txskb() on the other hand will (when required) call +ieee80211_report_ack_skb() and make sure that (when required) remove +the entry from the ack_status_frame and clean up related +information in its internal data structures. + +Signed-off-by: Sven Eckelmann + +Signed-off-by: Sarika Sharma +--- + drivers/net/wireless/ath/ath11k/dp_tx.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -451,7 +451,7 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + } + + if (unlikely(!skb_cb->vif)) { +- dev_kfree_skb_any(msdu); ++ ieee80211_free_txskb(ar->hw, msdu); + return; + } + 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..3de8497a321a2f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-233-ath11k-Disable-rx_header-tlv-for-2K-SKB.patch @@ -0,0 +1,973 @@ +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 +@@ -669,6 +669,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_rxdma_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; +@@ -1229,6 +1230,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_rxdma_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 +@@ -74,6 +74,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) + { +@@ -310,6 +316,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 +@@ -1283,6 +1283,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); +@@ -1292,6 +1294,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; +@@ -1370,6 +1392,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]; +@@ -288,6 +298,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 +@@ -6535,6 +6535,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_rxdma_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 +@@ -1508,4 +1508,16 @@ 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 +@@ -4363,7 +4363,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..7f92958691077e --- /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" +@@ -4345,6 +4346,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; + +@@ -4364,6 +4366,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; +@@ -4552,11 +4556,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); + +@@ -4585,6 +4591,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; +@@ -4597,6 +4612,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; +@@ -4890,6 +4906,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-fw-11-4-check.patch b/package/kernel/mac80211/patches/nss/ath11k/999-312-ath11k-add-fw-11-4-check.patch new file mode 100644 index 00000000000000..dd2af5bbf224b8 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-312-ath11k-add-fw-11-4-check.patch @@ -0,0 +1,31 @@ +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -818,9 +818,11 @@ ath11k_nss_vdev_special_data_receive(str + 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; ++#ifndef CONFIG_NSS_FIRMWARE_VERSION_11_4 ++ struct nss_wifi_vdev_addr4_data_metadata *addr4_metadata = NULL; + struct ath11k_peer *ta_peer = NULL; ++#endif + + arvif = ath11k_nss_get_arvif_from_dev(dev); + if (!arvif) { +@@ -872,6 +874,7 @@ ath11k_nss_vdev_special_data_receive(str + rxcb->is_mcbc = true; + ath11k_dp_rx_from_nss(arvif->ar, skb, napi); + break; ++#ifndef CONFIG_NSS_FIRMWARE_VERSION_11_4 + case NSS_WIFI_VDEV_EXT_DATA_PKT_TYPE_4ADDR: + if (eth_decap) { + addr4_metadata = &wifi_metadata->metadata.addr4_metadata; +@@ -891,6 +894,7 @@ ath11k_nss_vdev_special_data_receive(str + spin_unlock_bh(&ab->base_lock); + } + dev_kfree_skb_any(skb); ++#endif + break; + default: + ath11k_warn(ab, "unsupported pkt_type %d from nss\n", wifi_metadata->pkt_type); 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..abcc7a197e4300 --- /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, +@@ -6423,16 +6414,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..aa0f1f80af5d41 --- /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 +@@ -704,6 +704,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; +@@ -853,6 +858,7 @@ struct ath11k { + bool ps_state_enable; + bool ps_timekeeper_enable; + s8 max_allowed_tx_power; ++ 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 +@@ -910,7 +910,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 +@@ -389,7 +389,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; +@@ -397,9 +398,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]; +@@ -435,8 +442,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; +@@ -459,6 +472,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: +@@ -533,7 +552,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; + } +@@ -6176,12 +6219,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); + } + } + +@@ -6691,7 +6734,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 +@@ -12,6 +12,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) +@@ -126,7 +128,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-800-ath11k-Add-threaded-napi.patch b/package/kernel/mac80211/patches/nss/ath11k/999-800-ath11k-Add-threaded-napi.patch new file mode 100644 index 00000000000000..7abc6dc66c29e2 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-800-ath11k-Add-threaded-napi.patch @@ -0,0 +1,88 @@ +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -496,8 +496,7 @@ static int ath11k_ahb_ext_grp_napi_poll( + int work_done; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); +- if (work_done < budget) { +- napi_complete_done(napi, work_done); ++ if (work_done < budget && napi_complete_done(napi, work_done)) { + ath11k_ahb_ext_grp_enable(irq_grp); + } + +@@ -528,6 +527,7 @@ static int ath11k_ahb_config_ext_irq(str + int irq; + int ret; + bool nss_offload; ++ static int devidx = 0; + + /* TCL Completion, REO Dest, ERR, Exception and h2rxdma rings are offloaded + * to nss when its enabled, hence don't enable these interrupts +@@ -545,9 +545,14 @@ static int ath11k_ahb_config_ext_irq(str + if (!irq_grp->napi_ndev) + return -ENOMEM; + ++ snprintf(irq_grp->napi_ndev->name, sizeof(irq_grp->napi_ndev->name), "%s%d:%d", ++ "ath11k_ahb", devidx, i); ++ + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, + ath11k_ahb_ext_grp_napi_poll); + ++ dev_set_threaded(irq_grp->napi_ndev, true); ++ + for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { + if (!nss_offload && ab->hw_params.ring_mask->tx[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = +@@ -610,6 +615,8 @@ static int ath11k_ahb_config_ext_irq(str + } + } + ++ devidx++; ++ + return 0; + } + +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -517,8 +517,7 @@ static int ath11k_pcic_ext_grp_napi_poll + int i; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); +- if (work_done < budget) { +- napi_complete_done(napi, work_done); ++ if (work_done < budget && napi_complete_done(napi, work_done)) { + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); + } +@@ -563,6 +562,7 @@ static int ath11k_pcic_ext_irq_config(st + u32 user_base_data = 0, base_vector = 0; + struct ath11k_ext_irq_grp *irq_grp; + unsigned long irq_flags; ++ static int devidx = 0; + + ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors, + &user_base_data, +@@ -586,9 +586,14 @@ static int ath11k_pcic_ext_irq_config(st + goto fail_allocate; + } + ++ snprintf(irq_grp->napi_ndev->name, sizeof(irq_grp->napi_ndev->name), "%s%d:%d", ++ "ath11k_pci", devidx, i); ++ + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, + ath11k_pcic_ext_grp_napi_poll); + ++ dev_set_threaded(irq_grp->napi_ndev, true); ++ + /* tcl, reo, rx_err, wbm release, rxdma rings are offloaded to nss. */ + if (ab->nss.enabled && + !(ab->hw_params.ring_mask->reo_status[i] || +@@ -640,6 +645,8 @@ static int ath11k_pcic_ext_irq_config(st + ath11k_pcic_ext_grp_disable(irq_grp); + } + ++ devidx++; ++ + return 0; + fail_irq: + /* i ->napi_ndev was properly allocated. Free it also */ diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-802-ath11k-fixup-thermal-throttle.patch b/package/kernel/mac80211/patches/nss/ath11k/999-802-ath11k-fixup-thermal-throttle.patch new file mode 100644 index 00000000000000..6b9626b423f547 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-802-ath11k-fixup-thermal-throttle.patch @@ -0,0 +1,241 @@ +--- a/drivers/net/wireless/ath/ath11k/thermal.c ++++ b/drivers/net/wireless/ath/ath11k/thermal.c +@@ -135,16 +135,69 @@ int ath11k_thermal_set_throttling(struct + if (ar->state != ATH11K_STATE_ON) + return 0; + ++ ar->thermal.throttle_state = throttle_state; + memset(¶m, 0, sizeof(param)); + param.pdev_id = ar->pdev->pdev_id; + param.enable = throttle_state ? 1 : 0; + param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE; + param.dc_per_event = 0xFFFFFFFF; + +- param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK; +- param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK; +- param.levelconf[0].dcoffpercent = throttle_state; +- param.levelconf[0].priority = 0; /* disable all data tx queues */ ++ switch (ab->hw_rev) { ++ case ATH11K_HW_QCN9074_HW10: ++ param.levelconf[0].tmplwm = ++ ATH11K_THERMAL_TEMP0_LOW_MARK_QCN9000; ++ param.levelconf[0].tmphwm = ++ ATH11K_THERMAL_TEMP0_HIGH_MARK_QCN9000; ++ param.levelconf[1].tmplwm = ++ ATH11K_THERMAL_TEMP1_LOW_MARK_QCN9000; ++ param.levelconf[1].tmphwm = ++ ATH11K_THERMAL_TEMP1_HIGH_MARK_QCN9000; ++ param.levelconf[2].tmplwm = ++ ATH11K_THERMAL_TEMP2_LOW_MARK_QCN9000; ++ param.levelconf[2].tmphwm = ++ ATH11K_THERMAL_TEMP2_HIGH_MARK_QCN9000; ++ param.levelconf[3].tmplwm = ++ ATH11K_THERMAL_TEMP3_LOW_MARK_QCN9000; ++ param.levelconf[3].tmphwm = ++ ATH11K_THERMAL_TEMP3_HIGH_MARK_QCN9000; ++ break; ++ case ATH11K_HW_IPQ5018_HW10: ++ param.levelconf[0].tmplwm = ++ ATH11K_THERMAL_TEMP0_LOW_MARK_IPQ5018; ++ param.levelconf[0].tmphwm = ++ ATH11K_THERMAL_TEMP0_HIGH_MARK_IPQ5018; ++ param.levelconf[1].tmplwm = ++ ATH11K_THERMAL_TEMP1_LOW_MARK_IPQ5018; ++ param.levelconf[1].tmphwm = ++ ATH11K_THERMAL_TEMP1_HIGH_MARK_IPQ5018; ++ param.levelconf[2].tmplwm = ++ ATH11K_THERMAL_TEMP2_LOW_MARK_IPQ5018; ++ param.levelconf[2].tmphwm = ++ ATH11K_THERMAL_TEMP2_HIGH_MARK_IPQ5018; ++ param.levelconf[3].tmplwm = ++ ATH11K_THERMAL_TEMP3_LOW_MARK_IPQ5018; ++ param.levelconf[3].tmphwm = ++ ATH11K_THERMAL_TEMP3_HIGH_MARK_IPQ5018; ++ break; ++ default: ++ param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP0_LOW_MARK; ++ param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP0_HIGH_MARK; ++ param.levelconf[1].tmplwm = ATH11K_THERMAL_TEMP1_LOW_MARK; ++ param.levelconf[1].tmphwm = ATH11K_THERMAL_TEMP1_HIGH_MARK; ++ param.levelconf[2].tmplwm = ATH11K_THERMAL_TEMP2_LOW_MARK; ++ param.levelconf[2].tmphwm = ATH11K_THERMAL_TEMP2_HIGH_MARK; ++ param.levelconf[3].tmplwm = ATH11K_THERMAL_TEMP3_LOW_MARK; ++ param.levelconf[3].tmphwm = ATH11K_THERMAL_TEMP3_HIGH_MARK; ++ break; ++ } ++ param.levelconf[0].dcoffpercent = ATH11K_THERMAL_CONFIG_DCOFF0; ++ param.levelconf[0].priority = 7; /* all unicast frames */ ++ param.levelconf[1].dcoffpercent = ATH11K_THERMAL_CONFIG_DCOFF1; ++ param.levelconf[1].priority = 7; /* all unicast frames */ ++ param.levelconf[2].dcoffpercent = ATH11K_THERMAL_CONFIG_DCOFF2; ++ param.levelconf[2].priority = 7; /* all unicast frames */ ++ param.levelconf[3].dcoffpercent = ATH11K_THERMAL_CONFIG_DCOFF3; ++ param.levelconf[3].priority = 7; /* all unicast frames */ + + ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, ¶m); + if (ret) { +--- a/drivers/net/wireless/ath/ath11k/thermal.h ++++ b/drivers/net/wireless/ath/ath11k/thermal.h +@@ -7,8 +7,56 @@ + #ifndef _ATH11K_THERMAL_ + #define _ATH11K_THERMAL_ + +-#define ATH11K_THERMAL_TEMP_LOW_MARK -100 +-#define ATH11K_THERMAL_TEMP_HIGH_MARK 150 ++ ++#define ATH11K_THERMAL_MAX_TEMPERATURE_QCN9000 120 ++#define ATH11K_THERMAL_MAX_TEMPERATURE 150 ++ ++#define ATH11K_THERMAL_TEMP_INFINITE -100 ++#define ATH11K_THERMAL_TEMP0_LOW_MARK ATH11K_THERMAL_TEMP_INFINITE ++#define ATH11K_THERMAL_TEMP1_LOW_MARK 100 ++#define ATH11K_THERMAL_TEMP2_LOW_MARK 110 ++#define ATH11K_THERMAL_TEMP3_LOW_MARK 125 ++ ++#define ATH11K_THERMAL_TEMP0_HIGH_MARK 110 ++#define ATH11K_THERMAL_TEMP1_HIGH_MARK 120 ++#define ATH11K_THERMAL_TEMP2_HIGH_MARK 135 ++#define ATH11K_THERMAL_TEMP3_HIGH_MARK ATH11K_THERMAL_MAX_TEMPERATURE ++ ++#define ATH11K_THERMAL_TEMP0_LOW_MARK_QCN9000 ATH11K_THERMAL_TEMP_INFINITE ++#define ATH11K_THERMAL_TEMP1_LOW_MARK_QCN9000 95 ++#define ATH11K_THERMAL_TEMP2_LOW_MARK_QCN9000 100 ++#define ATH11K_THERMAL_TEMP3_LOW_MARK_QCN9000 105 ++ ++#define ATH11K_THERMAL_TEMP0_HIGH_MARK_QCN9000 100 ++#define ATH11K_THERMAL_TEMP1_HIGH_MARK_QCN9000 105 ++#define ATH11K_THERMAL_TEMP2_HIGH_MARK_QCN9000 110 ++#define ATH11K_THERMAL_TEMP3_HIGH_MARK_QCN9000 ATH11K_THERMAL_MAX_TEMPERATURE_QCN9000 ++ ++#define ATH11K_THERMAL_TEMP0_LOW_MARK_IPQ5018 ATH11K_THERMAL_TEMP_INFINITE ++#define ATH11K_THERMAL_TEMP1_LOW_MARK_IPQ5018 95 ++#define ATH11K_THERMAL_TEMP2_LOW_MARK_IPQ5018 100 ++#define ATH11K_THERMAL_TEMP3_LOW_MARK_IPQ5018 105 ++ ++#define ATH11K_THERMAL_TEMP0_HIGH_MARK_IPQ5018 105 ++#define ATH11K_THERMAL_TEMP1_HIGH_MARK_IPQ5018 110 ++#define ATH11K_THERMAL_TEMP2_HIGH_MARK_IPQ5018 115 ++#define ATH11K_THERMAL_TEMP3_HIGH_MARK_IPQ5018 ATH11K_THERMAL_MAX_TEMPERATURE_QCN9000 ++ ++#define ATH11K_THERMAL_CONFIG_DCOFF0 0 ++#define ATH11K_THERMAL_CONFIG_DCOFF1 50 ++#define ATH11K_THERMAL_CONFIG_DCOFF2 90 ++#define ATH11K_THERMAL_CONFIG_DCOFF3 100 ++ ++/* This is TX power reduction scaling factor in terms of 0.25db. ++ * Currently for all the levels from 1 to 4 in the enhanced thermal ++ * levels, the pout value will be reduced by -3dB (12 * 0.25). ++ * Customer will have option to configure the power out per thermal level. ++ */ ++#define ATH11K_THERMAL_CONFIG_POUT1 12 ++#define ATH11K_THERMAL_CONFIG_POUT2 12 ++#define ATH11K_THERMAL_CONFIG_POUT3 12 ++#define ATH11K_THERMAL_CONFIG_POUT4 12 ++ + #define ATH11K_THERMAL_THROTTLE_MAX 100 + #define ATH11K_THERMAL_DEFAULT_DUTY_CYCLE 100 + #define ATH11K_HWMON_NAME_LEN 15 +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -3236,6 +3236,7 @@ ath11k_wmi_send_thermal_mitigation_param + lvl_conf->temp_hwm = param->levelconf[i].tmphwm; + lvl_conf->dc_off_percent = param->levelconf[i].dcoffpercent; + lvl_conf->prio = param->levelconf[i].priority; ++ lvl_conf->pout_reduction_25db = 0; + lvl_conf++; + } + +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -4059,12 +4059,14 @@ struct wmi_11d_new_cc_ev { + u32 new_alpha2; + } __packed; + +-#define THERMAL_LEVELS 1 ++#define THERMAL_LEVELS 4 + struct tt_level_config { + u32 tmplwm; + u32 tmphwm; + u32 dcoffpercent; + u32 priority; ++ u32 pout_reduction_25db; ++ u32 tx_chain_mask; + }; + + struct thermal_mitigation_params { +@@ -4075,6 +4077,14 @@ struct thermal_mitigation_params { + struct tt_level_config levelconf[THERMAL_LEVELS]; + }; + ++typedef enum { ++ WMI_THERMAL_CLIENT_UNSPECIFIED = 0, ++ WMI_THERMAL_CLIENT_APPS = 1, ++ WMI_THERMAL_CLIENT_WPSS = 2, ++ WMI_THERMAL_CLIENT_FW = 3, ++ WMI_THERMAL_CLIENT_MAX ++} WMI_THERMAL_MITIGATION_CLIENTS; ++ + struct wmi_therm_throt_config_request_cmd { + u32 tlv_header; + u32 pdev_id; +@@ -4082,14 +4092,48 @@ struct wmi_therm_throt_config_request_cm + u32 dc; + u32 dc_per_event; + u32 therm_throt_levels; ++ u32 client_id; /* Indicates the client from whom the request is being forwarded to FW. Refer to WMI_THERMAL_MITIGATION_CLIENTS. */ ++ u32 priority; /* Indicates the priority, higher the value, higher the priority. Varies from 1 to WMI_THERMAL_CLIENT_MAX_PRIORITY. */ + } __packed; + + struct wmi_therm_throt_level_config_info { + u32 tlv_header; ++ /** temp_lwm: ++ * temperature sensor value in celsius when to exit to lower zone, ++ * this value can be lower than HWM of lower zone as zone overlapping ++ * is permitted by design to provide hysteresis ++ */ + u32 temp_lwm; ++ /** temp_hwm: ++ * temperature sensor value in celsius when to exit to higher zone, ++ * this value can be higher than LWM of higher zone as zone overlapping ++ * is permitted by design to provide hysteresis ++ */ + u32 temp_hwm; ++ /** dc_off_percent: ++ * duty cycle off percent 0-100. ++ * 0 means no off, 100 means no on (shutdown the phy). ++ */ + u32 dc_off_percent; ++ /** prio: ++ * Disable only the transmit queues in firmware that have lower priority ++ * than value defined by prio. ++ * Prioritization: ++ * 0 = disable all data tx queues, No Prioritization defined ++ * 1 = disable BK tx queue ++ * 2 = disable BK+BE tx queues ++ * 3 = disable BK+BE+VI tx queues ++ */ + u32 prio; ++ /** ++ * Pout power reduction in 0.25 dB units. ++ * For example, a value of 5 causes a power reduction of 1.25 dB. ++ */ ++ u32 pout_reduction_25db; ++ /** tx chain mask: ++ * Chain mask to apply based on the temp level ++ */ ++ u32 tx_chain_mask; + } __packed; + + struct wmi_delba_send_cmd { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6704,6 +6704,9 @@ static int ath11k_mac_op_start(struct ie + } + } + ++ /* set default thermal throttling */ ++ ath11k_thermal_set_throttling(ar, 1); ++ + mutex_unlock(&ar->conf_mutex); + + rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-900-bss-transition-handling.patch b/package/kernel/mac80211/patches/nss/ath11k/999-900-bss-transition-handling.patch new file mode 100644 index 00000000000000..1e70cacdc7883f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-900-bss-transition-handling.patch @@ -0,0 +1,2582 @@ +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -2050,10 +2050,10 @@ void ath11k_core_pre_reconfigure_recover + ar->monitor_vdev_id = -1; + clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); + clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ++ wake_up(&ar->peer_mapping_wq); + } + + wake_up(&ab->wmi_ab.tx_credits_wq); +- wake_up(&ab->peer_mapping_wq); + + reinit_completion(&ab->driver_recovery); + } +@@ -2329,7 +2329,6 @@ struct ath11k_base *ath11k_core_alloc(st + goto err_free_wq; + + 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); +@@ -2337,8 +2336,6 @@ struct ath11k_base *ath11k_core_alloc(st + init_completion(&ab->reconfigure_complete); + init_completion(&ab->recovery_start); + +- INIT_LIST_HEAD(&ab->peers); +- init_waitqueue_head(&ab->peer_mapping_wq); + init_waitqueue_head(&ab->wmi_ab.tx_credits_wq); + init_waitqueue_head(&ab->qmi.cold_boot_waitq); + INIT_WORK(&ab->restart_work, ath11k_core_restart); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -741,9 +741,22 @@ struct ath11k { + iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + } mac; + ++ /* To synchronize rhash tbl write operation */ ++ struct mutex tbl_mtx_lock; ++ ++ /* The rhashtable containing struct ath11k_peer keyed by mac addr */ ++ struct rhashtable *rhead_peer_addr; ++ struct rhashtable_params rhash_peer_addr_param; ++ ++ /* The rhashtable containing struct ath11k_peer keyed by id */ ++ struct rhashtable *rhead_peer_id; ++ struct rhashtable_params rhash_peer_id_param; ++ ++ + unsigned long dev_flags; + unsigned int filter_flags; + unsigned long monitor_flags; ++ u32 ack_timeout; + u32 min_tx_power; + u32 max_tx_power; + u32 txpower_limit_2g; +@@ -821,6 +834,9 @@ struct ath11k { + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + ++ struct list_head peers; ++ wait_queue_head_t peer_mapping_wq; ++ + struct ath11k_wow wow; + struct completion target_suspend; + bool target_suspend_ack; +@@ -1024,19 +1040,7 @@ struct ath11k_base { + struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; + unsigned long long free_vdev_map; + +- /* To synchronize rhash tbl write operation */ +- struct mutex tbl_mtx_lock; +- +- /* The rhashtable containing struct ath11k_peer keyed by mac addr */ +- struct rhashtable *rhead_peer_addr; +- struct rhashtable_params rhash_peer_addr_param; + +- /* The rhashtable containing struct ath11k_peer keyed by id */ +- struct rhashtable *rhead_peer_id; +- struct rhashtable_params rhash_peer_id_param; +- +- 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]; +@@ -1413,4 +1417,36 @@ static inline const char *ath11k_bus_str + return "unknown"; + } + ++static inline struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k *ar, ++ const u8 *addr) ++{ ++ struct ath11k_peer *peer; ++ ++ lockdep_assert_held(&ar->ab->base_lock); ++ ++ if (!ar->rhead_peer_addr) ++ return NULL; ++ ++ peer = rhashtable_lookup_fast(ar->rhead_peer_addr, addr, ++ ar->rhash_peer_addr_param); ++ ++ return peer; ++} ++ ++static inline struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k *ar, ++ int peer_id) ++{ ++ struct ath11k_peer *peer; ++ ++ lockdep_assert_held(&ar->ab->base_lock); ++ ++ if (!ar->rhead_peer_id) ++ return NULL; ++ ++ peer = rhashtable_lookup_fast(ar->rhead_peer_id, &peer_id, ++ ar->rhash_peer_id_param); ++ ++ return peer; ++} ++ + #endif /* _CORE_H_ */ +--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c +@@ -4161,7 +4161,7 @@ void htt_print_peer_ctrl_path_txrx_stats + stats_req->buf_len = len; + } + +-static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, ++static int ath11k_dbg_htt_ext_stats_parse(struct ath11k_base *ab, struct ath11k *ar, + u16 tag, u16 len, const void *tag_buf, + void *user_data) + { +@@ -4591,7 +4591,7 @@ void ath11k_debugfs_htt_ext_stats_handle + spin_unlock_bh(&ar->debug.htt_stats.lock); + + len = FIELD_GET(HTT_T2H_EXT_STATS_INFO1_LENGTH, msg->info1); +- ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, ++ ret = ath11k_dp_htt_tlv_iter(ab, ar, msg->data, len, + ath11k_dbg_htt_ext_stats_parse, + stats_req); + if (ret) +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -27,7 +27,7 @@ void ath11k_dp_peer_cleanup(struct ath11 + /* TODO: Any other peer specific DP cleanup */ + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find(ab, vdev_id, addr); ++ peer = ath11k_peer_find(ar, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to lookup peer %pM on vdev %d\n", + addr, vdev_id); +@@ -89,7 +89,7 @@ int ath11k_dp_peer_setup(struct ath11k * + peer_clean: + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, addr); ++ peer = ath11k_peer_find(ar, vdev_id, addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to del rx tid\n"); + spin_unlock_bh(&ab->base_lock); +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -564,7 +564,7 @@ static int ath11k_dp_rxdma_pdev_buf_setu + int i; + + /* RXDMA BUF ring is offloaded to NSS */ +- if (!ar->ab->nss.enabled) ++ if (!ab->nss.enabled) + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); + + if (ar->ab->hw_params.rxdma1_enable) { +@@ -1118,17 +1118,17 @@ static int ath11k_peer_rx_tid_reo_update + return 0; + } + +-static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, ++static void ath11k_dp_rx_tid_mem_free(struct ath11k *ar, + const u8 *peer_mac, int vdev_id, u8 tid) + { + struct ath11k_peer *peer; + struct dp_rx_tid *rx_tid; + +- spin_lock_bh(&ab->base_lock); ++ spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, peer_mac); ++ peer = ath11k_peer_find(ar, vdev_id, peer_mac); + if (!peer) { +- ath11k_warn(ab, "failed to find the peer to free up rx tid mem\n"); ++ ath11k_warn(ar->ab, "failed to find the peer to free up rx tid mem\n"); + goto unlock_exit; + } + +@@ -1136,7 +1136,7 @@ static void ath11k_dp_rx_tid_mem_free(st + if (!rx_tid->active) + goto unlock_exit; + +- dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, ++ dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; +@@ -1144,7 +1144,7 @@ static void ath11k_dp_rx_tid_mem_free(st + rx_tid->active = false; + + unlock_exit: +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->ab->base_lock); + } + + /* Sends WMI config to filter packets to route packets to WBM release ring */ +@@ -1185,7 +1185,7 @@ int ath11k_peer_rx_tid_setup(struct ath1 + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, peer_mac); ++ peer = ath11k_peer_find(ar, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer %pM to set up rx tid\n", + peer_mac); +@@ -1261,7 +1261,7 @@ int ath11k_peer_rx_tid_setup(struct ath1 + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n", + peer_mac, tid, ret); +- ath11k_dp_rx_tid_mem_free(ab, peer_mac, vdev_id, tid); ++ ath11k_dp_rx_tid_mem_free(ar, peer_mac, vdev_id, tid); + } + + return ret; +@@ -1304,7 +1304,7 @@ int ath11k_dp_rx_ampdu_stop(struct ath11 + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, params->sta->addr); ++ peer = ath11k_peer_find(ar, vdev_id, params->sta->addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to stop rx aggregation\n"); + spin_unlock_bh(&ab->base_lock); +@@ -1381,7 +1381,7 @@ int ath11k_dp_peer_rx_pn_replay_config(s + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, peer_addr); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to configure pn replay detection\n"); + spin_unlock_bh(&ab->base_lock); +@@ -1426,7 +1426,7 @@ static inline int ath11k_get_ppdu_user_i + return -EINVAL; + } + +-static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab, ++static int ath11k_htt_tlv_ppdu_stats_parse(struct ath11k_base *ab, struct ath11k *ar, + u16 tag, u16 len, const void *ptr, + void *data) + { +@@ -1558,10 +1558,9 @@ static int ath11k_htt_tlv_ppdu_stats_par + return 0; + } + +-static void ath11k_dp_ppdu_stats_flush_tlv_parse(struct ath11k_base *ab, ++static void ath11k_dp_ppdu_stats_flush_tlv_parse(struct ath11k_base *ab, struct ath11k *ar, + struct htt_ppdu_stats_cmpltn_flush *msg) + { +- struct ath11k *ar; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + struct ath11k_peer *peer = NULL; +@@ -1574,7 +1573,7 @@ static void ath11k_dp_ppdu_stats_flush_t + rcu_read_lock(); + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, msg->sw_peer_id); ++ peer = ath11k_peer_find_by_id(ar, msg->sw_peer_id); + if (!peer) + goto exit; + +@@ -1603,7 +1602,7 @@ exit: + rcu_read_unlock(); + } + +-static int ath11k_htt_tlv_ppdu_soc_stats_parse(struct ath11k_base *ab, ++static int ath11k_htt_tlv_ppdu_soc_stats_parse(struct ath11k_base *ab, struct ath11k *ar, + u16 tag, u16 len, const void *ptr, + void *data) + { +@@ -1614,7 +1613,7 @@ static int ath11k_htt_tlv_ppdu_soc_stats + len, tag); + return -EINVAL; + } +- ath11k_dp_ppdu_stats_flush_tlv_parse(ab, (struct htt_ppdu_stats_cmpltn_flush *)ptr); ++ ath11k_dp_ppdu_stats_flush_tlv_parse(ab, ar, (struct htt_ppdu_stats_cmpltn_flush *)ptr); + break; + default: + break; +@@ -1710,7 +1709,7 @@ ath11k_update_per_peer_tx_stats(struct a + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, usr_stats->peer_id); ++ peer = ath11k_peer_find_by_id(ar, usr_stats->peer_id); + + if (!peer || !peer->sta) { + spin_unlock_bh(&ab->base_lock); +@@ -1863,8 +1862,8 @@ void ath11k_copy_to_bar(struct ath11k_pe + 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, ++int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, struct ath11k *ar, const void *ptr, size_t len, ++ int (*iter)(struct ath11k_base *ab, struct ath11k *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) + { +@@ -1880,7 +1879,7 @@ int ath11k_dp_htt_tlv_iter(struct ath11k + } + while (len > 0) { + if (len < sizeof(*tlv)) { +- ath11k_err(ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", ++ ath11k_err(ar->ab, "htt tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } +@@ -1891,12 +1890,12 @@ int ath11k_dp_htt_tlv_iter(struct ath11k + len -= sizeof(*tlv); + + if (tlv_len > len) { +- ath11k_err(ab, "htt tlv parse failure of tag %hu at byte %zd (%zu bytes left, %hu expected)\n", ++ ath11k_err(ar->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; + } + +- ret = iter(ab, tlv_tag, tlv_len, ptr, ppdu_info); ++ ret = iter(ab, ar, tlv_tag, tlv_len, ptr, ppdu_info); + if (ret == -ENOMEM) + return ret; + +@@ -1923,7 +1922,7 @@ ath11k_dp_rx_ppdu_stats_update_tx_comp_s + + lockdep_assert_held(&ar->data_lock); + +- if (!ar->ab->nss.mesh_nss_offload_enabled) ++ if (!ab->nss.mesh_nss_offload_enabled) + return; + + ath11k_htt_update_ppdu_stats(ar, &ppdu_info->ppdu_stats); +@@ -1934,7 +1933,7 @@ ath11k_dp_rx_ppdu_stats_update_tx_comp_s + 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); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + continue; +@@ -1988,7 +1987,7 @@ static int ath11k_htt_pull_ppdu_stats(st + ppdu_id = msg->ppdu_id; + + if (pdev_id == 0) { +- ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, ++ ret = ath11k_dp_htt_tlv_iter(ab, ar, msg->data, len, + ath11k_htt_tlv_ppdu_soc_stats_parse, + NULL); + if (ret) +@@ -2014,7 +2013,7 @@ static int ath11k_htt_pull_ppdu_stats(st + } + + ppdu_info->ppdu_id = ppdu_id; +- ret = ath11k_dp_htt_tlv_iter(ab, msg->data, len, ++ ret = ath11k_dp_htt_tlv_iter(ab, ar, msg->data, len, + ath11k_htt_tlv_ppdu_stats_parse, + (void *)ppdu_info); + if (ret) { +@@ -2030,7 +2029,7 @@ static int ath11k_htt_pull_ppdu_stats(st + 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); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + continue; +@@ -2050,7 +2049,7 @@ static int ath11k_htt_pull_ppdu_stats(st + 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); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + continue; +@@ -2155,6 +2154,7 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + struct sk_buff *skb) + { + struct ath11k_dp *dp = &ab->dp; ++ struct ath11k *ar; + struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data; + enum htt_t2h_msg_type type = FIELD_GET(HTT_T2H_MSG_TYPE, *(u32 *)resp); + u16 peer_id; +@@ -2185,7 +2185,10 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + resp->peer_map_ev.info1); + ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); +- ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, 0, 0); ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); ++ ath11k_peer_map_event(ar, vdev_id, peer_id, mac_addr, 0, 0); ++ rcu_read_unlock(); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP2: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, +@@ -2202,17 +2205,27 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + resp->peer_map_ev.info1); + 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, ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); ++ ath11k_peer_map_v2_event(ar, vdev_id, peer_id, mac_addr, ast_hash, + hw_peer_id, is_wds); ++ rcu_read_unlock(); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); +- ath11k_peer_unmap_event(ab, peer_id); ++ vdev_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_VDEV_ID, ++ resp->peer_unmap_ev.info); ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); ++ ath11k_peer_unmap_event(ar, peer_id); ++ rcu_read_unlock(); + break; + case HTT_T2H_MSG_TYPE_PEER_UNMAP2: + peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, + resp->peer_unmap_ev.info); ++ vdev_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_VDEV_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, +@@ -2221,7 +2234,10 @@ void ath11k_dp_htt_htc_t2h_msg_handler(s + 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); ++ rcu_read_lock(); ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); ++ ath11k_peer_unmap_v2_event(ar, peer_id, mac_addr, is_wds, free_wds_count); ++ rcu_read_unlock(); + break; + case HTT_T2H_MSG_TYPE_PPDU_STATS_IND: + ath11k_htt_pull_ppdu_stats(ab, skb); +@@ -2876,25 +2892,25 @@ static void ath11k_dp_rx_h_undecap(struc + } + + static struct ath11k_peer * +-ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) ++ath11k_dp_rx_h_find_peer(struct ath11k *ar, struct sk_buff *msdu) + { + struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu); + struct hal_rx_desc *rx_desc = rxcb->rx_desc; + struct ath11k_peer *peer = NULL; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + + if (rxcb->peer_id) +- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); ++ peer = ath11k_peer_find_by_id(ar, rxcb->peer_id); + + if (peer) + return peer; + +- if (!rx_desc || !(ath11k_dp_rxdesc_mac_addr2_valid(ab, rx_desc))) ++ if (!rx_desc || !(ath11k_dp_rxdesc_mac_addr2_valid(ar->ab, rx_desc))) + return NULL; + +- peer = ath11k_peer_find_by_addr(ab, +- ath11k_dp_rxdesc_mpdu_start_addr2(ab, rx_desc)); ++ peer = ath11k_peer_find_by_addr(ar, ++ ath11k_dp_rxdesc_mpdu_start_addr2(ar->ab, rx_desc)); + return peer; + } + +@@ -2938,7 +2954,7 @@ static bool ath11k_dp_rx_check_fast_rx(s + 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); ++ f_peer = ath11k_peer_find_by_addr(ar, ehdr->h_dest); + + if (f_peer && f_peer != peer) + return false; +@@ -2977,7 +2993,7 @@ 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); ++ peer = ath11k_dp_rx_h_find_peer(ar, msdu); + if (peer) { + /* If the pkt is a valid IP packet and peer supports + * fast rx, deliver directly to net, also note that +@@ -3205,7 +3221,7 @@ static void ath11k_dp_rx_deliver_msdu(st + decap = ath11k_dp_rx_h_msdu_start_decap_type(ar->ab, rxcb->rx_desc); + + spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_dp_rx_h_find_peer(ar->ab, msdu); ++ peer = ath11k_dp_rx_h_find_peer(ar, msdu); + if (peer && peer->sta) + pubsta = peer->sta; + spin_unlock_bh(&ar->ab->base_lock); +@@ -3504,7 +3520,7 @@ try_again: + if (unlikely(push_reason != + HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) { + dev_kfree_skb_any(msdu); +- ab->soc_stats.hal_reo_error[dp->reo_dst_ring[ring_id].ring_id]++; ++ ab->soc_stats.hal_reo_error[ring_id]++; + continue; + } + +@@ -3736,7 +3752,7 @@ static void ath11k_dp_rx_update_user_sta + if (user_stats->ast_index == 0 || user_stats->ast_index == 0xFFFF) + return; + +- peer = ath11k_peer_find_by_ast(ar->ab, user_stats->ast_index); ++ peer = ath11k_peer_find_by_ast(ar, user_stats->ast_index); + + if (peer == NULL) { + if (!ar->ab->nss.enabled) +@@ -4210,7 +4226,7 @@ int ath11k_peer_rx_frag_setup(struct ath + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, peer_mac); ++ peer = ath11k_peer_find(ar, vdev_id, peer_mac); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to set up fragment info\n"); + spin_unlock_bh(&ab->base_lock); +@@ -4666,7 +4682,7 @@ static int ath11k_dp_rx_frag_h_mpdu(stru + return -EINVAL; + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, peer_id); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + if (!peer) { + ath11k_warn(ab, "failed to find the peer to de-fragment received fragment peer_id %d\n", + peer_id); +@@ -4728,7 +4744,7 @@ static int ath11k_dp_rx_frag_h_mpdu(stru + del_timer_sync(&rx_tid->frag_timer); + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_id(ab, peer_id); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + if (!peer) + goto err_frags_cleanup; + +@@ -6392,7 +6408,7 @@ int ath11k_dp_rx_process_mon_status(stru + trace_ath11k_htt_rxdesc(ar, skb->data, log_type, rx_buf_sz); + + ppdu_info->peer_id = HAL_INVALID_PEERID; +- hal_status = ath11k_hal_rx_parse_mon_status(ab, ppdu_info, skb); ++ hal_status = ath11k_hal_rx_parse_mon_status(ar, ppdu_info, skb); + + if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags) && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && +@@ -6423,7 +6439,7 @@ int ath11k_dp_rx_process_mon_status(stru + } + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ppdu_info->peer_id); ++ peer = ath11k_peer_find_by_id(ar, ppdu_info->peer_id); + + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, +--- a/drivers/net/wireless/ath/ath11k/dp_rx.h ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.h +@@ -130,8 +130,8 @@ int ath11k_dp_rxbufs_replenish(struct at + int req_entries, + 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, ++int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, struct ath11k *ar, const void *ptr, size_t len, ++ int (*iter)(struct ath11k_base *ab, struct ath11k *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data); + int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -482,7 +482,7 @@ ath11k_dp_tx_htt_tx_complete_buf(struct + } + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ts->peer_id); ++ peer = ath11k_peer_find_by_id(ar, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DATA, + "dp_tx: failed to find the peer with peer_id %d\n", +@@ -580,7 +580,7 @@ void ath11k_dp_tx_update_txcompl(struct + int ret; + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_id(ab, ts->peer_id); ++ peer = ath11k_peer_find_by_id(ar, ts->peer_id); + if (!peer || !peer->sta) { + ath11k_dbg(ab, ATH11K_DBG_DP_TX, + "failed to find the peer by id %u\n", ts->peer_id); +@@ -799,7 +799,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); ++ peer = ath11k_peer_find_by_id(ar, 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", +--- a/drivers/net/wireless/ath/ath11k/hal_rx.c ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.c +@@ -9,7 +9,6 @@ + #include "hal_tx.h" + #include "hal_rx.h" + #include "hal_desc.h" +-#include "hif.h" + + static void ath11k_hal_reo_set_desc_hdr(struct hal_desc_header *hdr, + u8 owner, u8 buffer_type, u32 magic) +@@ -875,7 +874,7 @@ static u16 ath11k_hal_rx_mpduinfo_get_pe + } + + static enum hal_rx_mon_status +-ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, ++ath11k_hal_rx_parse_mon_status_tlv(struct ath11k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + u32 tlv_tag, u8 *tlv_data, u32 userid) + { +@@ -1498,7 +1497,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + case HAL_PHYRX_RSSI_LEGACY: { + int i; + bool db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, +- ab->wmi_ab.svc_map); ++ ar->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; +@@ -1541,11 +1540,11 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + (struct hal_rx_mpdu_info *)tlv_data; + u16 peer_id; + +- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); ++ peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ar->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); ++ ppdu_info->mpdu_len += ar->ab->hw_params.hw_ops->rx_desc_get_hal_mpdu_len(mpdu_info); + + break; + } +@@ -1573,7 +1572,7 @@ ath11k_hal_rx_parse_mon_status_tlv(struc + } + + enum hal_rx_mon_status +-ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, ++ath11k_hal_rx_parse_mon_status(struct ath11k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct sk_buff *skb) + { +@@ -1599,7 +1598,7 @@ ath11k_hal_rx_parse_mon_status(struct at + if (tlv_tag == HAL_RX_PPDU_END) + tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + +- hal_status = ath11k_hal_rx_parse_mon_status_tlv(ab, ppdu_info, ++ hal_status = ath11k_hal_rx_parse_mon_status_tlv(ar, ppdu_info, + tlv_tag, ptr, tlv_userid); + ptr += tlv_len; + ptr = PTR_ALIGN(ptr, HAL_TLV_ALIGN); +--- a/drivers/net/wireless/ath/ath11k/hal_rx.h ++++ b/drivers/net/wireless/ath/ath11k/hal_rx.h +@@ -559,7 +559,7 @@ void + ath11k_hal_rx_sw_mon_ring_buf_paddr_get(void *rx_desc, + struct hal_sw_mon_ring_entries *sw_mon_ent); + enum hal_rx_mon_status +-ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab, ++ath11k_hal_rx_parse_mon_status(struct ath11k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct sk_buff *skb); + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -343,7 +343,7 @@ enum nl80211_he_gi ath11k_mac_he_gi_to_n + return ret; + } + +-static int ath11k_mac_cfg_dyn_vlan(struct ath11k_base *ab, ++static int ath11k_mac_cfg_dyn_vlan(struct ath11k *ar, + struct ath11k_vif *ap_vlan_arvif, + struct ieee80211_sta *sta); + +@@ -912,16 +912,16 @@ void ath11k_mac_peer_cleanup_all(struct + + lockdep_assert_held(&ar->conf_mutex); + +- mutex_lock(&ab->tbl_mtx_lock); ++ mutex_lock(&ar->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, &ar->peers, list) { + ath11k_peer_rx_tid_cleanup(ar, peer); +- ath11k_peer_rhash_delete(ab, peer); ++ ath11k_peer_rhash_delete(ar, peer); + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ab->base_lock); +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + + ar->num_peers = 0; + ar->num_stations = 0; +@@ -3208,7 +3208,7 @@ static void ath11k_bss_assoc(struct ieee + + spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, arvif->bssid); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, arvif->bssid); + if (peer && peer->is_authorized) + is_auth = true; + +@@ -4370,7 +4370,7 @@ static int ath11k_clear_peer_keys(struct + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find(ab, arvif->vdev_id, addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, addr); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + return -ENOENT; +@@ -4486,7 +4486,7 @@ static int ath11k_mac_op_set_key(struct + * we already hold conf_mutex. we just make sure its there now. + */ + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, peer_addr); + + /* flush the fragments cache during key (re)install to + * ensure all frags in the new frag list belong to the same key. +@@ -4595,7 +4595,7 @@ static int ath11k_mac_op_set_key(struct + } + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find(ab, arvif->vdev_id, peer_addr); ++ peer = ath11k_peer_find(ar, 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); +@@ -4626,7 +4626,7 @@ static int ath11k_mac_op_set_key(struct + 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); ++ ret = ath11k_mac_cfg_dyn_vlan(ar, 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); +@@ -5236,7 +5236,7 @@ static void ath11k_sta_set_4addr_wk(stru + vif = ap_vlan_arvif->vif; + + spin_lock_bh(&ab->base_lock); +- wds_peer = ath11k_peer_find_by_addr(ab, sta->addr); ++ wds_peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + if (!wds_peer) { + spin_unlock_bh(&ab->base_lock); + ath11k_warn(ab, "mac sta use 4addr failed to find peer %pM\n", +@@ -5459,7 +5459,7 @@ static void ath11k_mac_op_sta_rc_update( + + spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "mac sta rc update failed to find peer %pM on vdev %i\n", +@@ -8496,7 +8496,7 @@ ath11k_mac_op_assign_vif_chanctx(struct + if (ab->hw_params.vdev_start_delay && + arvif->vdev_type != WMI_VDEV_TYPE_AP && + arvif->vdev_type != WMI_VDEV_TYPE_MONITOR && +- !ath11k_peer_find_by_vdev_id(ab, arvif->vdev_id)) { ++ !ath11k_peer_find_by_vdev_id(ar, arvif->vdev_id)) { + memcpy(&arvif->chanctx, ctx, sizeof(*ctx)); + ret = 0; + goto out; +@@ -8572,7 +8572,7 @@ ath11k_mac_op_unassign_vif_chanctx(struc + if (ab->hw_params.vdev_start_delay && + arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_addr(ab, ar->mac_addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, ar->mac_addr); + spin_unlock_bh(&ab->base_lock); + if (peer) + ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr); +@@ -9146,7 +9146,7 @@ ath11k_mac_validate_vht_he_fixed_rate_se + + rcu_read_lock(); + spin_lock_bh(&ar->ab->base_lock); +- list_for_each_entry(peer, &ar->ab->peers, list) { ++ list_for_each_entry(peer, &ar->peers, list) { + if (peer->sta) { + deflink = &peer->sta->deflink; + +@@ -10102,26 +10102,26 @@ static int ath11k_mac_station_remove(str + return ret; + } + +-static int ath11k_mac_cfg_dyn_vlan(struct ath11k_base *ab, ++static int ath11k_mac_cfg_dyn_vlan(struct ath11k *ar, + 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); ++ spin_lock_bh(&ar->ab->base_lock); ++ peer = ath11k_peer_find_by_addr(ar, sta->addr); + if (!peer) { +- ath11k_warn(ab, "failed to find peer for %pM\n", sta->addr); +- spin_unlock_bh(&ab->base_lock); ++ ath11k_warn(ar->ab, "failed to find peer for %pM\n", sta->addr); ++ spin_unlock_bh(&ar->ab->base_lock); + return -EINVAL; + } + peer_id = peer->peer_id; +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->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", ++ ath11k_warn(ar->ab, "failed to set 4addr allow for %pM:%d\n", + sta->addr, ret); + return ret; + } +@@ -10171,20 +10171,20 @@ static int ath11k_mac_op_sta_state(struc + ath11k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + +- mutex_lock(&ar->ab->tbl_mtx_lock); ++ mutex_lock(&ar->tbl_mtx_lock); + spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + 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); ++ ath11k_peer_rhash_delete(ar, 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); ++ mutex_unlock(&ar->tbl_mtx_lock); + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || +@@ -10215,7 +10215,7 @@ static int ath11k_mac_op_sta_state(struc + new_state == IEEE80211_STA_AUTHORIZED) { + spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + if (peer) + peer->is_authorized = true; + +@@ -10252,7 +10252,7 @@ static int ath11k_mac_op_sta_state(struc + list_add_tail(&ar_dyn_vlan_cfg->cfg_list, &arvif->dyn_vlan_cfg); + } + } else { +- ret = ath11k_mac_cfg_dyn_vlan(ar->ab, arvif, sta); ++ ret = ath11k_mac_cfg_dyn_vlan(ar, arvif, sta); + if (ret) + ath11k_warn(ar->ab, "failed to cfg dyn vlan for peer %pM: %d\n", + sta->addr, ret); +@@ -10285,7 +10285,7 @@ static int ath11k_mac_op_sta_state(struc + new_state == IEEE80211_STA_ASSOC) { + + spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + if (peer) + peer->is_authorized = false; + spin_unlock_bh(&ar->ab->base_lock); +@@ -10293,7 +10293,7 @@ static int ath11k_mac_op_sta_state(struc + new_state == IEEE80211_STA_ASSOC) { + spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr); ++ peer = ath11k_peer_find(ar, arvif->vdev_id, sta->addr); + if (peer) + peer->is_authorized = false; + +@@ -10701,9 +10701,9 @@ void ath11k_mac_unregister(struct ath11k + continue; + + __ath11k_mac_unregister(ar); ++ ath11k_peer_rhash_tbl_destroy(ar); + } + +- ath11k_peer_rhash_tbl_destroy(ab); + } + + static int __ath11k_mac_register(struct ath11k *ar) +@@ -10987,15 +10987,15 @@ int ath11k_mac_register(struct ath11k_ba + ab->cc_freq_hz = IPQ8074_CC_FREQ_HERTZ; + ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS(ab))) - 1; + +- ret = ath11k_peer_rhash_tbl_init(ab); +- if (ret) +- return ret; + + device_get_mac_address(ab->dev, mac_addr); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; ++ ret = ath11k_peer_rhash_tbl_init(ar); ++ if (ret) ++ return ret; + if (ab->pdevs_macaddr_valid) { + ether_addr_copy(ar->mac_addr, pdev->mac_addr); + } else { +@@ -11023,9 +11023,9 @@ err_cleanup: + pdev = &ab->pdevs[i]; + ar = pdev->ar; + __ath11k_mac_unregister(ar); ++ ath11k_peer_rhash_tbl_destroy(ar); + } + +- ath11k_peer_rhash_tbl_destroy(ab); + + return ret; + } +@@ -11069,9 +11069,12 @@ int ath11k_mac_allocate(struct ath11k_ba + ar->num_rx_chains = get_num_chains(pdev->cap.rx_chain_mask); + + pdev->ar = ar; ++ mutex_init(&ar->tbl_mtx_lock); + spin_lock_init(&ar->data_lock); + INIT_LIST_HEAD(&ar->arvifs); + INIT_LIST_HEAD(&ar->ppdu_stats_info); ++ INIT_LIST_HEAD(&ar->peers); ++ init_waitqueue_head(&ar->peer_mapping_wq); + mutex_init(&ar->conf_mutex); + init_completion(&ar->vdev_setup_done); + init_completion(&ar->vdev_delete_done); +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -143,7 +143,7 @@ static void ath11k_nss_get_peer_stats(st + { + struct ath11k_peer *peer; + struct nss_wifili_peer_ctrl_stats *pstats = NULL; +- int i, j; ++ int i, j, i2; + u64 tx_packets, tx_bytes, tx_dropped = 0; + u64 rx_packets, rx_bytes, rx_dropped; + +@@ -156,7 +156,13 @@ static void ath11k_nss_get_peer_stats(st + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_id(ab, pstats->peer_id); ++ for (i2 = 0; i2 < ab->num_radios; i2++) { ++ struct ath11k_pdev *pdev = &ab->pdevs[i2]; ++ struct ath11k *ar = pdev->ar; ++ peer = ath11k_peer_find_by_id(ar, pstats->peer_id); ++ if (peer) ++ break; ++ } + 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); +@@ -278,28 +284,28 @@ static void ath11k_nss_tx_encap_raw(stru + static void ath11k_nss_peer_mem_free(struct ath11k_base *ab, u32 peer_id) + { + struct ath11k_peer *peer; ++ struct ath11k *ar; ++ int i; + + 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; +- } ++ for (i = 0; i < ab->num_radios; i++) { ++ struct ath11k_pdev *pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ peer = ath11k_peer_find_by_id(ar, peer_id); ++ if (!peer) ++ continue; + +- dma_unmap_single(ab->dev, peer->nss.paddr, +- WIFILI_NSS_PEER_BYTE_SIZE, DMA_FROM_DEVICE); ++ 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; ++ kfree(peer->nss.vaddr); ++ if (peer->nss.nss_stats) { ++ kfree(peer->nss.nss_stats); ++ peer->nss.nss_stats = NULL; ++ } ++ complete(&peer->nss.complete); + } +- +- complete(&peer->nss.complete); + spin_unlock_bh(&ab->base_lock); + + ath11k_dbg(ab, ATH11K_DBG_NSS, "nss peer %d mem freed\n", peer_id); +@@ -420,6 +426,7 @@ void ath11k_nss_wifili_event_receive(str + void ath11k_nss_process_mic_error(struct ath11k_base *ab, struct sk_buff *skb) + { + struct ath11k_vif *arvif; ++ struct ath11k *ar; + struct ath11k_peer *peer = NULL; + struct hal_rx_desc *desc = (struct hal_rx_desc *)skb->data; + struct wireless_dev *wdev; +@@ -427,6 +434,7 @@ void ath11k_nss_process_mic_error(struct + u8 peer_addr[ETH_ALEN]; + u8 ucast_keyidx, mcast_keyidx; + bool is_mcbc; ++ int i; + + if (!ath11k_dp_rx_h_msdu_end_first_msdu(ab, desc)) + goto fail; +@@ -435,7 +443,13 @@ void ath11k_nss_process_mic_error(struct + 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); ++ for (i = 0; i < ab->num_radios; i++) { ++ struct ath11k_pdev *pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ peer = ath11k_peer_find_by_id(ar, peer_id); ++ if (peer) ++ break; ++ } + if (!peer) { + ath11k_info(ab, "ath11k_nss:peer not found"); + spin_unlock_bh(&ab->base_lock); +@@ -604,7 +618,7 @@ static int ath11k_nss_undecap_raw(struct + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_addr(ab, hdr->addr2); ++ peer = ath11k_peer_find_by_addr(ar, hdr->addr2); + if (!peer) { + ath11k_warn(ab, "peer not found for raw/nwifi undecap, drop this packet\n"); + spin_unlock_bh(&ab->base_lock); +@@ -646,16 +660,48 @@ static int ath11k_nss_undecap_nwifi(stru + return 0; + } + +-static void ath11k_nss_wds_type_rx(struct ath11k *ar, struct net_device *dev, ++static bool vdev_check_local_dev(u8 *wds_src_mac) ++{ ++ struct net_device *dev = NULL; ++ ++ rcu_read_lock(); ++ for_each_netdev_rcu(&init_net, dev) { ++ if (!dev) { ++ continue; ++ } ++ if (!memcmp(dev->dev_addr, wds_src_mac, 6)) { ++ rcu_read_unlock(); ++ return true; ++ } ++ } ++ rcu_read_unlock(); ++ return false; ++} ++ ++static void ath11k_nss_wds_type_rx(struct ath11k_vif *arvif, struct net_device *dev, + u8* src_mac, u8 is_sa_valid, u8 addr4_valid, + u16 peer_id) + { ++ struct ath11k *ar = arvif->ar; + struct ath11k_base *ab = ar->ab; + struct ath11k_ast_entry *ast_entry = NULL; + struct ath11k_peer *ta_peer = NULL; ++ struct wireless_dev *wdev = NULL; ++ struct ieee80211_vif *vif = NULL; + + spin_lock_bh(&ab->base_lock); +- ta_peer = ath11k_peer_find_by_id(ab, peer_id); ++ ++ wdev = dev->ieee80211_ptr; ++ if (!wdev) { ++ return; ++ } ++ ++ vif = wdev_to_ieee80211_vif(wdev); ++ if (!vif) { ++ return; ++ } ++ ++ ta_peer = ath11k_peer_find_by_id(ar, peer_id); + + if (!ta_peer) { + spin_unlock_bh(&ab->base_lock); +@@ -666,7 +712,32 @@ static void ath11k_nss_wds_type_rx(struc + ta_peer->addr); + + if (addr4_valid) { +- ast_entry = ath11k_peer_ast_find_by_addr(ab, src_mac); ++ ast_entry = ath11k_peer_ast_find_by_addr(ar, src_mac); ++ ++ /* ++ * If WDS update is coming back on same peer it indicates that it is not roamed ++ * This situation can happen if a MEC packet reached in Rx direction even before the ++ * ast entry installation in happend in HW ++ */ ++ if (ast_entry) { ++ if (ast_entry->peer && (ast_entry->peer->peer_id == ta_peer->peer_id) && (vif->type == NL80211_IFTYPE_STATION)) { ++ spin_unlock_bh(&ab->base_lock); ++ return; ++ } ++ } ++ ++ /* ++ * Avoid WDS learning if src mac address matches ++ * any of local netdevice mac address. ++ */ ++ if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_AP_VLAN) { ++ if (vdev_check_local_dev(src_mac)) { ++ spin_unlock_bh(&ab->base_lock); ++ return; ++ } ++ } ++ ++ + if (!is_sa_valid) { + ath11k_peer_add_ast(ar, ta_peer, src_mac, + ATH11K_AST_TYPE_WDS); +@@ -749,7 +820,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, ++ ath11k_nss_wds_type_rx(arvif, skb->dev, src_mac, is_sa_valid, + addr4_valid, peer_id); + break; + case NSS_WIFI_VDEV_WDS_TYPE_MEC: +@@ -774,7 +845,7 @@ static bool ath11k_nss_vdev_data_receive + src_mac); + + spin_lock_bh(&ab->base_lock); +- ast_entry = ath11k_peer_ast_find_by_addr(ab, src_mac); ++ ast_entry = ath11k_peer_ast_find_by_addr(ar, src_mac); + + if (ast_entry && ast_entry->type == ATH11K_AST_TYPE_MEC) { + spin_unlock_bh(&ab->base_lock); +@@ -880,7 +951,7 @@ ath11k_nss_vdev_special_data_receive(str + 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); ++ ta_peer = ath11k_peer_find_by_id(arvif->ar, addr4_metadata->peer_id); + if (!ta_peer) { + spin_unlock_bh(&ab->base_lock); + dev_kfree_skb_any(skb); +@@ -3485,7 +3556,7 @@ void ath11k_nss_update_sta_stats(struct + return; + + spin_lock_bh(&ab->base_lock); +- peer = ath11k_peer_find_by_addr(ab, sta->addr); ++ peer = ath11k_peer_find_by_addr(ar, sta->addr); + if (!peer) { + ath11k_dbg(ab, ATH11K_DBG_NSS, "sta stats: unable to find peer %pM\n", + sta->addr); +@@ -3642,7 +3713,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, u32 vdev_id, const u8 *addr) ++int ath11k_nss_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr) + { + struct nss_wifili_peer_msg *peer_msg; + struct nss_wifili_msg *wlmsg = NULL; +@@ -3651,29 +3722,29 @@ int ath11k_nss_peer_delete(struct ath11k + nss_tx_status_t status; + int ret; + +- if (!ab->nss.enabled) ++ if (!ar->ab->nss.enabled) + return 0; + +- spin_lock_bh(&ab->base_lock); ++ spin_lock_bh(&ar->ab->base_lock); + +- peer = ath11k_peer_find(ab, vdev_id, addr); ++ peer = ath11k_peer_find(ar, vdev_id, addr); + if (!peer) { +- ath11k_warn(ab, "peer (%pM) not found on vdev_id %d for nss peer delete\n", ++ ath11k_warn(ar->ab, "peer (%pM) not found on vdev_id %d for nss peer delete\n", + addr, vdev_id); +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->ab->base_lock); + return -EINVAL; + } + + if (!peer->nss.vaddr) { +- ath11k_warn(ab, "peer already deleted or peer create failed %pM\n", ++ ath11k_warn(ar->ab, "peer already deleted or peer create failed %pM\n", + addr); +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->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"); ++ ath11k_warn(ar->ab, "nss send peer delete msg alloc failure\n"); + ret = -ENOMEM; + goto free_peer; + } +@@ -3685,27 +3756,27 @@ int ath11k_nss_peer_delete(struct ath11k + + msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; + +- nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, ++ nss_cmn_msg_init(&wlmsg->cm, ar->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); ++ status = nss_wifili_tx_msg(ar->ab->nss.ctx, wlmsg); + if (status != NSS_TX_SUCCESS) { +- ath11k_warn(ab, "nss send peer (%pM) delete msg tx error %d\n", ++ ath11k_warn(ar->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", ++ ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer delete message success : peer_id %d\n", + peer->peer_id); + ret = 0; + } + +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->ab->base_lock); + + kfree(wlmsg); + +@@ -3715,20 +3786,20 @@ int ath11k_nss_peer_delete(struct ath11k + */ + 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"); ++ if (ar->ab->nss.debug_mode && !ret) ++ ath11k_warn(ar->ab, "timeout while waiting for nss peer delete msg response\n"); + + return 0; + + free_peer: +- dma_unmap_single(ab->dev, peer->nss.paddr, ++ dma_unmap_single(ar->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); ++ spin_unlock_bh(&ar->ab->base_lock); + return ret; + } + +@@ -4467,10 +4538,11 @@ static int ath11k_nss_init(struct ath11k + 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) ++ if (ab->nss.response != ATH11K_NSS_MSG_ACK) { ++ ath11k_warn(ab, "non ack response from nss received (%d)\n", ab->nss.response); + goto unregister; ++ } + + kfree(wlmsg); + +@@ -4580,6 +4652,7 @@ int ath11k_nss_pdev_init(struct ath11k_b + * for messages related to vdev/radio + */ + ar->nss.if_num = radio_if_num; ++ spin_lock_init(&ar->nss.dump_lock); + + /* No callbacks are registered for radio specific events/data */ + ar->nss.ctx = nss_register_wifili_radio_if((u32)radio_if_num, NULL, +--- a/drivers/net/wireless/ath/ath11k/nss.h ++++ b/drivers/net/wireless/ath/ath11k/nss.h +@@ -283,7 +283,7 @@ 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, u32 vdev_id, const u8 *addr); ++int ath11k_nss_peer_delete(struct ath11k *ar, 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 *ar, struct ath11k_peer *peer); + void ath11k_nss_peer_stats_enable(struct ath11k *ar); +@@ -393,7 +393,7 @@ static inline int ath11k_nss_vdev_down(s + return 0; + } + +-static inline int ath11k_nss_peer_delete(struct ath11k_base *ab, u32 vdev_id, ++static inline int ath11k_nss_peer_delete(struct ath11k *ar, u32 vdev_id, + const u8 *addr) + { + return 0; +--- a/drivers/net/wireless/ath/ath11k/peer.c ++++ b/drivers/net/wireless/ath/ath11k/peer.c +@@ -9,14 +9,14 @@ + #include "debug.h" + #include "nss.h" + +-static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab, ++static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k *ar, + int peer_id) + { + struct ath11k_peer *peer; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) { ++ list_for_each_entry(peer, &ar->peers, list) { + if (peer->peer_id != peer_id) + continue; + +@@ -26,14 +26,14 @@ static struct ath11k_peer *ath11k_peer_f + return NULL; + } + +-struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, ++struct ath11k_peer *ath11k_peer_find(struct ath11k *ar, int vdev_id, + const u8 *addr) + { + struct ath11k_peer *peer; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) { ++ list_for_each_entry(peer, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + if (!ether_addr_equal(peer->addr, addr)) +@@ -45,63 +45,31 @@ struct ath11k_peer *ath11k_peer_find(str + return NULL; + } + +-struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, +- const u8 *addr) +-{ +- struct ath11k_peer *peer; +- +- lockdep_assert_held(&ab->base_lock); +- +- if (!ab->rhead_peer_addr) +- return NULL; +- +- peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr, +- ab->rhash_peer_addr_param); +- +- return peer; +-} +- +-struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, +- int peer_id) +-{ +- struct ath11k_peer *peer; +- +- lockdep_assert_held(&ab->base_lock); +- +- if (!ab->rhead_peer_id) +- return NULL; +- +- peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id, +- ab->rhash_peer_id_param); +- +- return peer; +-} +- +-struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, ++struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k *ar, + int vdev_id) + { + struct ath11k_peer *peer; + +- spin_lock_bh(&ab->base_lock); ++ spin_lock_bh(&ar->ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) { ++ list_for_each_entry(peer, &ar->peers, list) { + if (vdev_id == peer->vdev_id) { +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->ab->base_lock); + return peer; + } + } +- spin_unlock_bh(&ab->base_lock); ++ spin_unlock_bh(&ar->ab->base_lock); + return NULL; + } + +-struct ath11k_peer *ath11k_peer_find_by_ast(struct ath11k_base *ab, ++struct ath11k_peer *ath11k_peer_find_by_ast(struct ath11k *ar, + int ast_hash) + { + struct ath11k_peer *peer; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) ++ list_for_each_entry(peer, &ar->peers, list) + if (ast_hash == peer->ast_hash) + return peer; + +@@ -109,13 +77,13 @@ struct ath11k_peer *ath11k_peer_find_by_ + } + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT +-struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k_base *ab, ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k *ar, + struct ath11k_peer *peer, + u8* addr) + { + struct ath11k_ast_entry *ast_entry; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + + list_for_each_entry(ast_entry, &peer->ast_entry_list, ase_list) + if (ether_addr_equal(ast_entry->addr, addr)) +@@ -124,15 +92,15 @@ struct ath11k_ast_entry *ath11k_peer_ast + return NULL; + } + +-struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k *ar, + u8* addr) + { + struct ath11k_ast_entry *ast_entry; + struct ath11k_peer *peer; + +- lockdep_assert_held(&ab->base_lock); ++ lockdep_assert_held(&ar->ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) ++ list_for_each_entry(peer, &ar->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; +@@ -149,7 +117,7 @@ struct ath11k_ast_entry *ath11k_peer_ast + + lockdep_assert_held(&ab->base_lock); + +- list_for_each_entry(peer, &ab->peers, list) ++ list_for_each_entry(peer, &ar->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) +@@ -186,7 +154,7 @@ void ath11k_peer_ast_wds_wmi_wk(struct w + memcpy(peer_addr, peer->addr, sizeof(peer_addr)); + peer_id = peer->peer_id; + +- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, ++ ath11k_dbg(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); +@@ -198,7 +166,7 @@ void ath11k_peer_ast_wds_wmi_wk(struct w + ast_entry->vdev_id, + true); + if (ret) { +- ath11k_warn(ar->ab, "add wds_entry_cmd failed %d for %pM, peer %pM\n", ++ ath11k_warn(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, +@@ -214,7 +182,7 @@ void ath11k_peer_ast_wds_wmi_wk(struct w + ast_entry->vdev_id, + false); + if (ret) +- ath11k_warn(ar->ab, "update wds_entry_cmd failed %d for %pM on peer %pM\n", ++ ath11k_warn(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); +@@ -363,7 +331,7 @@ void ath11k_peer_map_ast(struct ath11k * + if (!peer) + return; + +- ast_entry = ath11k_peer_ast_find_by_peer(ab, peer, mac_addr); ++ ast_entry = ath11k_peer_ast_find_by_peer(ar, peer, mac_addr); + + if (ast_entry) { + ast_entry->ast_idx = hw_peer_id; +@@ -418,6 +386,7 @@ void ath11k_peer_ast_cleanup(struct ath1 + bool is_wds, u32 free_wds_count) + { + struct ath11k_ast_entry *ast_entry, *tmp; ++ struct ath11k_base *ab = ar->ab; + u32 ast_deleted_count = 0; + + if (peer->self_ast_entry) { +@@ -435,22 +404,23 @@ void ath11k_peer_ast_cleanup(struct ath1 + + 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", ++ ath11k_warn(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", ++ ath11k_dbg(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) ++void ath11k_peer_unmap_event(struct ath11k *ar, u16 peer_id) + { + struct ath11k_peer *peer; ++ struct ath11k_base *ab = ar->ab; + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_list_by_id(ab, peer_id); ++ peer = ath11k_peer_find_list_by_id(ar, peer_id); + if (!peer) { + ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", + peer_id); +@@ -462,34 +432,27 @@ void ath11k_peer_unmap_event(struct ath1 + + list_del(&peer->list); + kfree(peer); +- wake_up(&ab->peer_mapping_wq); ++ wake_up(&ar->peer_mapping_wq); + + exit: + spin_unlock_bh(&ab->base_lock); + } + +-void ath11k_peer_unmap_v2_event(struct ath11k_base *ab, u16 peer_id, u8 *mac_addr, ++void ath11k_peer_unmap_v2_event(struct ath11k *ar, u16 peer_id, u8 *mac_addr, + bool is_wds, u32 free_wds_count) + { + struct ath11k_peer *peer; +- struct ath11k *ar; ++ struct ath11k_base *ab = ar->ab; + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_list_by_id(ab, peer_id); ++ peer = ath11k_peer_find_list_by_id(ar, 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); +@@ -497,11 +460,10 @@ void ath11k_peer_unmap_v2_event(struct a + if (ab->nss.enabled) { + if (is_wds) { + struct ath11k_ast_entry *ast_entry = +- ath11k_peer_ast_find_by_peer(ab, peer, mac_addr); ++ ath11k_peer_ast_find_by_peer(ar, 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); +@@ -511,26 +473,22 @@ void ath11k_peer_unmap_v2_event(struct a + 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); ++ wake_up(&ar->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, ++void ath11k_peer_map_event(struct ath11k *ar, u8 vdev_id, u16 peer_id, + u8 *mac_addr, u16 ast_hash, u16 hw_peer_id) + { + struct ath11k_peer *peer; +- struct ath11k *ar = NULL; ++ struct ath11k_base *ab = ar->ab; + +- 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); ++ peer = ath11k_peer_find(ar, vdev_id, mac_addr); + if (!peer) { + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) +@@ -541,8 +499,8 @@ void ath11k_peer_map_event(struct ath11k + 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); +- wake_up(&ab->peer_mapping_wq); ++ list_add(&peer->list, &ar->peers); ++ wake_up(&ar->peer_mapping_wq); + if (ab->nss.enabled && ar) + ath11k_nss_peer_create(ar, peer); + } +@@ -552,27 +510,23 @@ 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, ++void ath11k_peer_map_v2_event(struct ath11k *ar, 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; ++ struct ath11k_base *ab = ar->ab; + 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); ++ peer = ath11k_peer_find(ar, 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; + } + +@@ -581,7 +535,7 @@ void ath11k_peer_map_v2_event(struct ath + 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); ++ list_add(&peer->list, &ar->peers); + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + INIT_LIST_HEAD(&peer->ast_entry_list); + #endif +@@ -593,11 +547,11 @@ void ath11k_peer_map_v2_event(struct ath + goto peer_free; + } + } +- wake_up(&ab->peer_mapping_wq); ++ wake_up(&ar->peer_mapping_wq); + } + + if (is_wds) +- peer = ath11k_peer_find_by_id(ab, peer_id); ++ peer = ath11k_peer_find_by_id(ar, peer_id); + + if (ab->nss.enabled && ar) + ath11k_peer_map_ast(ar, peer, mac_addr, hw_peer_id, ast_hash); +@@ -605,7 +559,6 @@ void ath11k_peer_map_v2_event(struct ath + 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: +@@ -614,23 +567,23 @@ peer_free: + ath11k_peer_delete(ar, vdev_id, mac_addr); + mutex_unlock(&ar->conf_mutex); + exit: +- rcu_read_unlock(); ++ spin_unlock_bh(&ab->base_lock); + } + +-static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, ++static int ath11k_wait_for_peer_common(struct ath11k *ar, int vdev_id, + const u8 *addr, bool expect_mapped) + { + int ret; + +- ret = wait_event_timeout(ab->peer_mapping_wq, ({ ++ ret = wait_event_timeout(ar->peer_mapping_wq, ({ + bool mapped; + +- spin_lock_bh(&ab->base_lock); +- mapped = !!ath11k_peer_find(ab, vdev_id, addr); +- spin_unlock_bh(&ab->base_lock); ++ spin_lock_bh(&ar->ab->base_lock); ++ mapped = !!ath11k_peer_find(ar, vdev_id, addr); ++ spin_unlock_bh(&ar->ab->base_lock); + + (mapped == expect_mapped || +- test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)); ++ test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)); + }), 3 * HZ); + + if (ret <= 0) +@@ -639,7 +592,7 @@ static int ath11k_wait_for_peer_common(s + return 0; + } + +-static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab, ++static inline int ath11k_peer_rhash_insert(struct ath11k *ar, + struct rhashtable *rtbl, + struct rhash_head *rhead, + struct rhashtable_params *params, +@@ -647,7 +600,7 @@ static inline int ath11k_peer_rhash_inse + { + struct ath11k_peer *tmp; + +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + + tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params); + +@@ -659,14 +612,14 @@ static inline int ath11k_peer_rhash_inse + return -EEXIST; + } + +-static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab, ++static inline int ath11k_peer_rhash_remove(struct ath11k *ar, + struct rhashtable *rtbl, + struct rhash_head *rhead, + struct rhashtable_params *params) + { + int ret; + +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + + ret = rhashtable_remove_fast(rtbl, rhead, *params); + if (ret && ret != -ENOENT) +@@ -675,26 +628,27 @@ static inline int ath11k_peer_rhash_remo + return 0; + } + +-static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer) ++static int ath11k_peer_rhash_add(struct ath11k *ar, struct ath11k_peer *peer) + { + int ret; ++ struct ath11k_base *ab = ar->ab; + + lockdep_assert_held(&ab->base_lock); +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (!ab->rhead_peer_id || !ab->rhead_peer_addr) ++ if (!ar->rhead_peer_id || !ar->rhead_peer_addr) + return -EPERM; + +- ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id, +- &ab->rhash_peer_id_param, &peer->peer_id); ++ ret = ath11k_peer_rhash_insert(ar, ar->rhead_peer_id, &peer->rhash_id, ++ &ar->rhash_peer_id_param, &peer->peer_id); + if (ret) { + ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n", + peer->addr, peer->peer_id, ret); + return ret; + } + +- ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr, +- &ab->rhash_peer_addr_param, &peer->addr); ++ ret = ath11k_peer_rhash_insert(ar, ar->rhead_peer_addr, &peer->rhash_addr, ++ &ar->rhash_peer_addr_param, &peer->addr); + if (ret) { + ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n", + peer->addr, peer->peer_id, ret); +@@ -704,8 +658,8 @@ static int ath11k_peer_rhash_add(struct + return 0; + + err_clean: +- ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id, +- &ab->rhash_peer_id_param); ++ ath11k_peer_rhash_remove(ar, ar->rhead_peer_id, &peer->rhash_id, ++ &ar->rhash_peer_id_param); + return ret; + } + +@@ -723,9 +677,9 @@ void ath11k_peer_cleanup(struct ath11k * + mutex_lock(&ab->base_ast_lock); + #endif + +- mutex_lock(&ab->tbl_mtx_lock); ++ mutex_lock(&ar->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); +- list_for_each_entry_safe(peer, tmp_peer, &ab->peers, list) { ++ list_for_each_entry_safe(peer, tmp_peer, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + +@@ -743,14 +697,14 @@ void ath11k_peer_cleanup(struct ath11k * + ath11k_peer_del_ast(ar, ast_entry); + #endif + +- ath11k_peer_rhash_delete(ab, peer); ++ ath11k_peer_rhash_delete(ar, peer); + list_del(&peer->list); + kfree(peer); + ar->num_peers--; + } + + spin_unlock_bh(&ab->base_lock); +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + mutex_unlock(&ab->base_ast_lock); + #endif +@@ -758,25 +712,26 @@ void ath11k_peer_cleanup(struct ath11k * + + static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) + { +- return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false); ++ return ath11k_wait_for_peer_common(ar, vdev_id, addr, false); + } + + int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, + const u8 *addr) + { ++ struct ath11k_base *ab = ar->ab; + int ret; + unsigned long time_left; + + ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) { +- ath11k_warn(ar->ab, "failed wait for peer deleted"); ++ ath11k_warn(ab, "failed wait for peer deleted"); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 3 * HZ); + if (time_left == 0) { +- ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n"); ++ ath11k_warn(ab, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + +@@ -795,26 +750,22 @@ 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, vdev_id, addr); ++ ath11k_nss_peer_delete(ar, vdev_id, addr); + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + mutex_lock(&ab->base_ast_lock); + #endif +- mutex_lock(&ab->tbl_mtx_lock); ++ mutex_lock(&ar->tbl_mtx_lock); + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_addr(ab, addr); +- +- /* 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 +- * been deleted in peer add. +- */ +- if (!peer) +- peer = ath11k_peer_find(ab, vdev_id, addr); ++ peer = ath11k_peer_find(ar, vdev_id, addr); + + if (!peer) { + spin_unlock_bh(&ab->base_lock); +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); ++#ifdef CPTCFG_ATH11K_NSS_SUPPORT ++ mutex_unlock(&ab->base_ast_lock); ++#endif + + ath11k_warn(ab, + "failed to find peer vdev_id %d addr %pM in delete\n", +@@ -835,18 +786,18 @@ static int __ath11k_peer_delete(struct a + 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, ++ ath11k_dbg(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); ++ ath11k_peer_rhash_delete(ar, peer); + } + + spin_unlock_bh(&ab->base_lock); +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT + mutex_unlock(&ab->base_ast_lock); +@@ -884,7 +835,7 @@ int ath11k_peer_delete(struct ath11k *ar + + static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr) + { +- return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true); ++ return ath11k_wait_for_peer_common(ar, vdev_id, addr, true); + } + + int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, +@@ -892,38 +843,41 @@ int ath11k_peer_create(struct ath11k *ar + { + struct ath11k_peer *peer; + struct ieee80211_vif *vif = arvif->vif; ++ struct ath11k_base *ab = ar->ab; + struct ath11k_sta *arsta; + int ret, fbret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->num_peers > (ar->max_num_peers - 1)) { +- ath11k_warn(ar->ab, ++ ath11k_warn(ab, + "failed to create peer due to insufficient peer entry resource in firmware\n"); + return -ENOBUFS; + } + +- mutex_lock(&ar->ab->tbl_mtx_lock); +- spin_lock_bh(&ar->ab->base_lock); +- peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr); +- if (peer) { +- if (peer->vdev_id == param->vdev_id) { +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); +- return -EINVAL; +- } ++ mutex_lock(&ar->tbl_mtx_lock); ++ spin_lock_bh(&ab->base_lock); + +- /* Assume sta is transitioning to another band. +- * Remove here the peer from rhash. +- */ +- ath11k_peer_rhash_delete(ar->ab, peer); ++ /* try exact match first to prevent double addition */ ++ peer = ath11k_peer_find(ar, param->vdev_id, param->peer_addr); ++ if (peer) { ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); ++ return -EINVAL; + } +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); ++ /* try loose match now and check if peer mac is already associated at another bssid on the same mac */ ++ peer = ath11k_peer_find_by_addr(ar, param->peer_addr); ++ if (peer) { ++ /* if found, remove it from the hash list, so it wont be handled by datapath anymore, since we expect a disassoc soon */ ++ peer->delete_in_progress = true; ++ ath11k_peer_rhash_delete(ar, peer); ++ } ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + + ret = ath11k_wmi_send_peer_create_cmd(ar, param); + if (ret) { +- ath11k_warn(ar->ab, ++ ath11k_warn(ab, + "failed to send peer create vdev_id %d ret %d\n", + param->vdev_id, ret); + return ret; +@@ -934,24 +888,23 @@ int ath11k_peer_create(struct ath11k *ar + if (ret) + return ret; + +- mutex_lock(&ar->ab->tbl_mtx_lock); +- spin_lock_bh(&ar->ab->base_lock); ++ mutex_lock(&ar->tbl_mtx_lock); ++ spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); ++ peer = ath11k_peer_find(ar, param->vdev_id, param->peer_addr); + if (!peer) { +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); +- ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); ++ ath11k_warn(ab, "failed to find peer %pM on vdev %i after creation\n", + param->peer_addr, param->vdev_id); +- + ret = -ENOENT; + goto cleanup; + } + +- ret = ath11k_peer_rhash_add(ar->ab, peer); ++ ret = ath11k_peer_rhash_add(ar, peer); + if (ret) { +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + goto cleanup; + } + +@@ -968,7 +921,7 @@ int ath11k_peer_create(struct ath11k *ar + peer->vif = arvif->vif; + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT +- if (vif->type == NL80211_IFTYPE_STATION && ar->ab->nss.enabled) ++ if (vif->type == NL80211_IFTYPE_STATION && ab->nss.enabled) + ar->bss_peer = peer; + else + ar->bss_peer = NULL; +@@ -986,40 +939,41 @@ int ath11k_peer_create(struct ath11k *ar + + ar->num_peers++; + +- spin_unlock_bh(&ar->ab->base_lock); +- mutex_unlock(&ar->ab->tbl_mtx_lock); ++ spin_unlock_bh(&ab->base_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + + return 0; + + cleanup: + fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr); + if (fbret) +- ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n", ++ ath11k_warn(ab, "failed peer %pM delete vdev_id %d fallback ret %d\n", + param->peer_addr, param->vdev_id, fbret); + + return ret; + } + +-int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer) ++int ath11k_peer_rhash_delete(struct ath11k *ar, struct ath11k_peer *peer) + { ++ struct ath11k_base *ab = ar->ab; + int ret; + + lockdep_assert_held(&ab->base_lock); +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (!ab->rhead_peer_id || !ab->rhead_peer_addr) ++ if (!ar->rhead_peer_id || !ar->rhead_peer_addr) + return -EPERM; + +- ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr, +- &ab->rhash_peer_addr_param); ++ ret = ath11k_peer_rhash_remove(ar, ar->rhead_peer_addr, &peer->rhash_addr, ++ &ar->rhash_peer_addr_param); + if (ret) { + ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n", + peer->addr, peer->peer_id, ret); + return ret; + } + +- ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id, +- &ab->rhash_peer_id_param); ++ ret = ath11k_peer_rhash_remove(ar, ar->rhead_peer_id, &peer->rhash_id, ++ &ar->rhash_peer_id_param); + if (ret) { + ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n", + peer->addr, peer->peer_id, ret); +@@ -1029,19 +983,20 @@ int ath11k_peer_rhash_delete(struct ath1 + return 0; + } + +-static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab) ++static int ath11k_peer_rhash_id_tbl_init(struct ath11k *ar) + { ++ struct ath11k_base *ab = ar->ab; + struct rhashtable_params *param; + struct rhashtable *rhash_id_tbl; + int ret; + size_t size; + +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (ab->rhead_peer_id) ++ if (ar->rhead_peer_id) + return 0; + +- size = sizeof(*ab->rhead_peer_id); ++ size = sizeof(*ar->rhead_peer_id); + rhash_id_tbl = kzalloc(size, GFP_KERNEL); + if (!rhash_id_tbl) { + ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n", +@@ -1049,13 +1004,13 @@ static int ath11k_peer_rhash_id_tbl_init + return -ENOMEM; + } + +- param = &ab->rhash_peer_id_param; ++ param = &ar->rhash_peer_id_param; + + param->key_offset = offsetof(struct ath11k_peer, peer_id); + param->head_offset = offsetof(struct ath11k_peer, rhash_id); + param->key_len = sizeof_field(struct ath11k_peer, peer_id); + param->automatic_shrinking = true; +- param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab); ++ param->nelem_hint = TARGET_NUM_PEERS_PDEV(ab); + + ret = rhashtable_init(rhash_id_tbl, param); + if (ret) { +@@ -1063,10 +1018,10 @@ static int ath11k_peer_rhash_id_tbl_init + goto err_free; + } + +- spin_lock_bh(&ab->base_lock); ++ spin_lock_bh(&ab->base_lock); // todo removw + +- if (!ab->rhead_peer_id) { +- ab->rhead_peer_id = rhash_id_tbl; ++ if (!ar->rhead_peer_id) { ++ ar->rhead_peer_id = rhash_id_tbl; + } else { + spin_unlock_bh(&ab->base_lock); + goto cleanup_tbl; +@@ -1084,19 +1039,20 @@ err_free: + return ret; + } + +-static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab) ++static int ath11k_peer_rhash_addr_tbl_init(struct ath11k *ar) + { ++ struct ath11k_base *ab = ar->ab; + struct rhashtable_params *param; + struct rhashtable *rhash_addr_tbl; + int ret; + size_t size; + +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (ab->rhead_peer_addr) ++ if (ar->rhead_peer_addr) + return 0; + +- size = sizeof(*ab->rhead_peer_addr); ++ size = sizeof(*ar->rhead_peer_addr); + rhash_addr_tbl = kzalloc(size, GFP_KERNEL); + if (!rhash_addr_tbl) { + ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n", +@@ -1104,13 +1060,13 @@ static int ath11k_peer_rhash_addr_tbl_in + return -ENOMEM; + } + +- param = &ab->rhash_peer_addr_param; ++ param = &ar->rhash_peer_addr_param; + + param->key_offset = offsetof(struct ath11k_peer, addr); + param->head_offset = offsetof(struct ath11k_peer, rhash_addr); + param->key_len = sizeof_field(struct ath11k_peer, addr); + param->automatic_shrinking = true; +- param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab); ++ param->nelem_hint = TARGET_NUM_PEERS_PDEV(ab); + + ret = rhashtable_init(rhash_addr_tbl, param); + if (ret) { +@@ -1118,10 +1074,10 @@ static int ath11k_peer_rhash_addr_tbl_in + goto err_free; + } + +- spin_lock_bh(&ab->base_lock); ++ spin_lock_bh(&ab->base_lock); // todo remove + +- if (!ab->rhead_peer_addr) { +- ab->rhead_peer_addr = rhash_addr_tbl; ++ if (!ar->rhead_peer_addr) { ++ ar->rhead_peer_addr = rhash_addr_tbl; + } else { + spin_unlock_bh(&ab->base_lock); + goto cleanup_tbl; +@@ -1139,61 +1095,61 @@ err_free: + return ret; + } + +-static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab) ++static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k *ar) + { +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (!ab->rhead_peer_id) ++ if (!ar->rhead_peer_id) + return; + +- rhashtable_destroy(ab->rhead_peer_id); +- kfree(ab->rhead_peer_id); +- ab->rhead_peer_id = NULL; ++ rhashtable_destroy(ar->rhead_peer_id); ++ kfree(ar->rhead_peer_id); ++ ar->rhead_peer_id = NULL; + } + +-static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab) ++static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k *ar) + { +- lockdep_assert_held(&ab->tbl_mtx_lock); ++ lockdep_assert_held(&ar->tbl_mtx_lock); + +- if (!ab->rhead_peer_addr) ++ if (!ar->rhead_peer_addr) + return; + +- rhashtable_destroy(ab->rhead_peer_addr); +- kfree(ab->rhead_peer_addr); +- ab->rhead_peer_addr = NULL; ++ rhashtable_destroy(ar->rhead_peer_addr); ++ kfree(ar->rhead_peer_addr); ++ ar->rhead_peer_addr = NULL; + } + +-int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab) ++int ath11k_peer_rhash_tbl_init(struct ath11k *ar) + { + int ret; + +- mutex_lock(&ab->tbl_mtx_lock); ++ mutex_lock(&ar->tbl_mtx_lock); + +- ret = ath11k_peer_rhash_id_tbl_init(ab); ++ ret = ath11k_peer_rhash_id_tbl_init(ar); + if (ret) + goto out; + +- ret = ath11k_peer_rhash_addr_tbl_init(ab); ++ ret = ath11k_peer_rhash_addr_tbl_init(ar); + if (ret) + goto cleanup_tbl; + +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + + return 0; + + cleanup_tbl: +- ath11k_peer_rhash_id_tbl_destroy(ab); ++ ath11k_peer_rhash_id_tbl_destroy(ar); + out: +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + return ret; + } + +-void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab) ++void ath11k_peer_rhash_tbl_destroy(struct ath11k *ar) + { +- mutex_lock(&ab->tbl_mtx_lock); ++ mutex_lock(&ar->tbl_mtx_lock); + +- ath11k_peer_rhash_addr_tbl_destroy(ab); +- ath11k_peer_rhash_id_tbl_destroy(ab); ++ ath11k_peer_rhash_addr_tbl_destroy(ar); ++ ath11k_peer_rhash_id_tbl_destroy(ar); + +- mutex_unlock(&ab->tbl_mtx_lock); ++ mutex_unlock(&ar->tbl_mtx_lock); + } +--- a/drivers/net/wireless/ath/ath11k/peer.h ++++ b/drivers/net/wireless/ath/ath11k/peer.h +@@ -98,34 +98,32 @@ struct ath11k_peer { + bool delete_in_progress; + }; + +-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, ++ ++void ath11k_peer_unmap_event(struct ath11k *ar, u16 peer_id); ++void ath11k_peer_unmap_v2_event(struct ath11k *ar, 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, ++void ath11k_peer_map_event(struct ath11k *ar, 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, ++void ath11k_peer_map_v2_event(struct ath11k *ar, 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, ++struct ath11k_peer *ath11k_peer_find(struct ath11k *at, int vdev_id, + const u8 *addr); +-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); ++struct ath11k_peer *ath11k_peer_find_by_ast(struct ath11k *ar, 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, + struct ieee80211_sta *sta, struct peer_create_params *param); + int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, + const u8 *addr); +-struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, ++struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k *ar, + int vdev_id); +-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); ++int ath11k_peer_rhash_tbl_init(struct ath11k *ar); ++void ath11k_peer_rhash_tbl_destroy(struct ath11k *ar); ++int ath11k_peer_rhash_delete(struct ath11k *ar, struct ath11k_peer *peer); + + #ifdef CPTCFG_ATH11K_NSS_SUPPORT +-struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k *ar, + u8* addr); + struct ath11k_ast_entry *ath11k_peer_ast_find_by_pdev_idx(struct ath11k *ar, + u8* addr); +@@ -139,11 +137,11 @@ void ath11k_peer_del_ast(struct ath11k * + 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_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k *ar, + struct ath11k_peer *peer, + u8* addr); + #else +-static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k_base *ab, ++static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_addr(struct ath11k *ar, + u8* addr) + { + return NULL; +@@ -191,7 +189,7 @@ static inline void ath11k_peer_ast_wds_w + return; + } + +-static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k_base *ab, ++static inline struct ath11k_ast_entry *ath11k_peer_ast_find_by_peer(struct ath11k *ar, + struct ath11k_peer *peer, + u8* addr) + { +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -7626,10 +7626,12 @@ static void ath11k_wmi_event_peer_sta_ps + struct ieee80211_sta *sta; + struct ath11k_peer *peer; + struct ath11k *ar; ++ struct ath11k_pdev *pdev; + struct ath11k_sta *arsta; + const void **tb; + enum ath11k_wmi_peer_ps_state peer_previous_ps_state; + int ret; ++ int i; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { +@@ -7653,64 +7655,60 @@ static void ath11k_wmi_event_peer_sta_ps + + rcu_read_lock(); + +- spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_addr(ab, ev->peer_macaddr.addr); ++ for (i = 0; i < ab->num_radios; i++) { ++ spin_lock_bh(&ab->base_lock); ++ pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ peer = ath11k_peer_find_by_addr(ar, ev->peer_macaddr.addr); ++ if (!peer) { ++ spin_unlock_bh(&ab->base_lock); ++ continue; ++ } + +- if (!peer) { +- spin_unlock_bh(&ab->base_lock); +- ath11k_warn(ab, "peer not found %pM\n", ev->peer_macaddr.addr); +- goto exit; +- } ++ ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); + +- ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); ++ if (!ar) { ++ spin_unlock_bh(&ab->base_lock); ++ ath11k_warn(ab, "invalid vdev id in peer sta ps state change ev %d", peer->vdev_id); + +- if (!ar) { +- spin_unlock_bh(&ab->base_lock); +- ath11k_warn(ab, "invalid vdev id in peer sta ps state change ev %d", +- peer->vdev_id); ++ goto exit; ++ } + +- goto exit; +- } ++ sta = peer->sta; + +- sta = peer->sta; ++ spin_unlock_bh(&ab->base_lock); + +- spin_unlock_bh(&ab->base_lock); ++ if (!sta) { ++ ath11k_warn(ab, "failed to find station entry %pM\n", ev->peer_macaddr.addr); ++ goto exit; ++ } + +- if (!sta) { +- ath11k_warn(ab, "failed to find station entry %pM\n", +- ev->peer_macaddr.addr); +- goto exit; +- } ++ arsta = ath11k_sta_to_arsta(sta); + +- arsta = ath11k_sta_to_arsta(sta); ++ spin_lock_bh(&ar->data_lock); + +- spin_lock_bh(&ar->data_lock); ++ peer_previous_ps_state = arsta->peer_ps_state; ++ arsta->peer_ps_state = ev->peer_ps_state; ++ arsta->peer_current_ps_valid = !!ev->peer_ps_valid; ++ ++ if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT, ar->ab->wmi_ab.svc_map)) { ++ if (!(ev->ps_supported_bitmap & WMI_PEER_PS_VALID) || ++ !(ev->ps_supported_bitmap & WMI_PEER_PS_STATE_TIMESTAMP) || !ev->peer_ps_valid) ++ goto out; ++ ++ if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON) { ++ arsta->ps_start_time = ev->peer_ps_timestamp; ++ arsta->ps_start_jiffies = jiffies; ++ } else if (arsta->peer_ps_state == WMI_PEER_PS_STATE_OFF && ++ peer_previous_ps_state == WMI_PEER_PS_STATE_ON) { ++ arsta->ps_total_duration = ++ arsta->ps_total_duration + (ev->peer_ps_timestamp - arsta->ps_start_time); ++ } + +- peer_previous_ps_state = arsta->peer_ps_state; +- arsta->peer_ps_state = ev->peer_ps_state; +- arsta->peer_current_ps_valid = !!ev->peer_ps_valid; +- +- if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT, +- ar->ab->wmi_ab.svc_map)) { +- if (!(ev->ps_supported_bitmap & WMI_PEER_PS_VALID) || +- !(ev->ps_supported_bitmap & WMI_PEER_PS_STATE_TIMESTAMP) || +- !ev->peer_ps_valid) +- goto out; +- +- if (arsta->peer_ps_state == WMI_PEER_PS_STATE_ON) { +- arsta->ps_start_time = ev->peer_ps_timestamp; +- arsta->ps_start_jiffies = jiffies; +- } else if (arsta->peer_ps_state == WMI_PEER_PS_STATE_OFF && +- peer_previous_ps_state == WMI_PEER_PS_STATE_ON) { +- arsta->ps_total_duration = arsta->ps_total_duration + +- (ev->peer_ps_timestamp - arsta->ps_start_time); ++ if (ar->ps_timekeeper_enable) ++ trace_ath11k_ps_timekeeper(ar, ev->peer_macaddr.addr, ev->peer_ps_timestamp, arsta->peer_ps_state); + } +- +- if (ar->ps_timekeeper_enable) +- trace_ath11k_ps_timekeeper(ar, ev->peer_macaddr.addr, +- ev->peer_ps_timestamp, +- arsta->peer_ps_state); + } + + out: +@@ -7830,9 +7828,9 @@ static void ath11k_mgmt_rx_event(struct + + spin_lock_bh(&ab->base_lock); + +- peer = ath11k_peer_find_by_addr(ab, hdr->addr1); ++ peer = ath11k_peer_find_by_addr(ar, hdr->addr1); + if(!peer) +- peer = ath11k_peer_find_by_addr(ab, hdr->addr3); ++ peer = ath11k_peer_find_by_addr(ar, hdr->addr3); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + goto skip_mgmt_stats; +@@ -8060,9 +8058,8 @@ static void ath11k_peer_sta_kickout_even + { + struct wmi_peer_sta_kickout_arg arg = {}; + struct ieee80211_sta *sta; +- struct ath11k_peer *peer; + struct ath11k *ar; +- u32 vdev_id; ++ int i; + + if (ath11k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) { + ath11k_warn(ab, "failed to extract peer sta kickout event"); +@@ -8071,42 +8068,22 @@ static void ath11k_peer_sta_kickout_even + + rcu_read_lock(); + +- spin_lock_bh(&ab->base_lock); +- +- peer = ath11k_peer_find_by_addr(ab, arg.mac_addr); + +- if (!peer) { +- ath11k_warn(ab, "peer not found %pM\n", +- arg.mac_addr); +- spin_unlock_bh(&ab->base_lock); +- goto exit; +- } +- +- vdev_id = peer->vdev_id; +- +- spin_unlock_bh(&ab->base_lock); ++ for (i = 0; i < ab->num_radios; i++) { ++ struct ath11k_pdev *pdev = &ab->pdevs[i]; ++ ar = pdev->ar; ++ sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL); ++ if (!sta) { ++ continue; ++ } + +- ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id); +- if (!ar) { +- ath11k_warn(ab, "invalid vdev id in peer sta kickout ev %d", +- peer->vdev_id); +- goto exit; +- } ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "event peer sta kickout %pM", arg.mac_addr); + +- sta = ieee80211_find_sta_by_ifaddr(ar->hw, +- arg.mac_addr, NULL); +- if (!sta) { +- ath11k_warn(ab, "Spurious quick kickout for STA %pM\n", +- arg.mac_addr); +- goto exit; ++ ieee80211_report_low_ack(sta, 10); + } ++ if (!sta) ++ ath11k_dbg(ab, ATH11K_DBG_WMI, "Spurious quick kickout for STA %pM\n", arg.mac_addr); + +- ath11k_dbg(ab, ATH11K_DBG_WMI, "event peer sta kickout %pM", +- arg.mac_addr); +- +- ieee80211_report_low_ack(sta, 10); +- +-exit: + rcu_read_unlock(); + } + diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-901-ath11k-NSS-use-ath11k_nss_get_arvif_from_dev.patch b/package/kernel/mac80211/patches/nss/ath11k/999-901-ath11k-NSS-use-ath11k_nss_get_arvif_from_dev.patch new file mode 100644 index 00000000000000..b50dcdd0d88508 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-901-ath11k-NSS-use-ath11k_nss_get_arvif_from_dev.patch @@ -0,0 +1,51 @@ +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -978,36 +978,20 @@ ath11k_nss_vdev_data_receive(struct net_ + __attribute__((unused)) struct napi_struct *napi) + { + 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; ++ arvif = ath11k_nss_get_arvif_from_dev(dev); + if (!arvif) { + dev_kfree_skb_any(skb); + return; + } + ++ wdev = ieee80211_vif_to_wdev_relaxed(arvif->vif); ++ + ab = arvif->ar->ab; + + skb->dev = dev; +@@ -1016,7 +1000,7 @@ ath11k_nss_vdev_data_receive(struct net_ + ath11k_dbg_dump(ab, ATH11K_DBG_DP_RX, "", "dp rx msdu from nss: ", + skb->data, skb->len); + +- if ((vif->type == NL80211_IFTYPE_STATION && wdev->use_4addr) && ++ if ((arvif->vif->type == NL80211_IFTYPE_STATION && wdev->use_4addr) && + ath11k_nss_vdev_data_receive_mec_check(arvif->ar, skb)) { + dev_kfree_skb_any(skb); + return; diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-902-ath11k-fix-WDS-by-disabling-nwds.patch b/package/kernel/mac80211/patches/nss/ath11k/999-902-ath11k-fix-WDS-by-disabling-nwds.patch new file mode 100644 index 00000000000000..2fa2b082dbeac1 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-902-ath11k-fix-WDS-by-disabling-nwds.patch @@ -0,0 +1,76 @@ +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -5295,14 +5295,19 @@ skip_nss_ext: + * 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); ++ WMI_VDEV_PARAM_WDS, ++ 1); + if (ret) { +- ath11k_warn(ab, "failed to set vdev %i nawds parameters: %d\n", ++ ath11k_warn(ab, "failed to set vdev %i wds parameters: %d\n", + arvif->vdev_id, ret); + goto ext_vdev_down; + } + ++ 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); ++ } ++ + return; + + ext_vdev_down: +@@ -7239,6 +7244,23 @@ static int ath11k_mac_op_add_interface(s + goto err_vdev_del; + } + ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ WMI_VDEV_PARAM_WDS, 1); ++ if (ret) { ++ ath11k_warn(ab, "failed to enable wds%d\n", ret); ++ } ++ ++ ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, ++ WMI_VDEV_PARAM_CAPABILITIES, ++ WMI_VDEV_WDS_LRN_ENABLED); ++ if (ret) { ++ ath11k_warn( ++ ab, ++ "failed to set vdev %d capability 0x%x, nss %d :%d\n", ++ arvif->vdev_id, WMI_VDEV_WDS_LRN_ENABLED, nss, 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, + WMI_VDEV_PARAM_NSS, nss); +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -3255,6 +3255,11 @@ int ath11k_nss_ext_vdev_create(struct at + "nss ext vdev interface created ctx %pK, ifnum %d\n", + arvif->nss.ctx, arvif->nss.if_num); + ++ ret = ath11k_nss_vdev_set_cmd(arvif, ATH11K_NSS_WIFI_VDEV_CFG_WDS_BACKHAUL_CMD, true); ++ if (ret) { ++ ath11k_warn(ab, "failed to enable wds backhaul %d\n", ret); ++ } ++ + return ret; + + free_ext_vdev: +--- a/drivers/net/wireless/ath/ath11k/wmi.h ++++ b/drivers/net/wireless/ath/ath11k/wmi.h +@@ -1112,6 +1112,10 @@ enum wmi_tlv_vdev_param { + WMI_VDEV_PARAM_HEOPS_0_31 = 0x8003, + }; + ++#define WMI_VDEV_BEACON_SUPPORT 0x1 ++#define WMI_VDEV_WDS_LRN_ENABLED 0x2 ++#define WMI_VDEV_VOW_ENABLED 0x4 ++ + enum wmi_tlv_peer_flags { + WMI_PEER_AUTH = 0x00000001, + WMI_PEER_QOS = 0x00000002, diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-903-ath11k-use-ath11k_sta_to_arsta.patch b/package/kernel/mac80211/patches/nss/ath11k/999-903-ath11k-use-ath11k_sta_to_arsta.patch new file mode 100644 index 00000000000000..ae3b44597e19ee --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-903-ath11k-use-ath11k_sta_to_arsta.patch @@ -0,0 +1,70 @@ +--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c ++++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c +@@ -1074,7 +1074,7 @@ static ssize_t ath11k_dbg_sta_reset_rx_s + 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_sta *arsta = ath11k_sta_to_arsta(sta); + struct ath11k *ar = arsta->arvif->ar; + int ret, reset; + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1584,7 +1584,7 @@ static void ath11k_dp_ppdu_stats_flush_t + goto exit; + + sta = peer->sta; +- arsta = (struct ath11k_sta *)sta->drv_priv; ++ arsta = ath11k_sta_to_arsta(sta); + + memset(&status, 0, sizeof(status)); + +@@ -1950,7 +1950,7 @@ ath11k_dp_rx_ppdu_stats_update_tx_comp_s + } + + sta = peer->sta; +- arsta = (struct ath11k_sta *)sta->drv_priv; ++ arsta = ath11k_sta_to_arsta(sta); + + memset(&status, 0, sizeof(status)); + +@@ -3009,8 +3009,7 @@ static void ath11k_dp_rx_h_mpdu(struct a + 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; ++ arsta = ath11k_sta_to_arsta(peer->sta); + return; + } + } +@@ -3761,7 +3760,7 @@ static void ath11k_dp_rx_update_user_sta + return; + } + +- arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ arsta = ath11k_sta_to_arsta(peer->sta); + rx_stats = arsta->rx_stats; + + if (ar->ab->nss.enabled) +@@ -6451,7 +6450,7 @@ int ath11k_dp_rx_process_mon_status(stru + if ((ppdu_info->fc_valid) && + (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { + if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { +- arsta = (struct ath11k_sta *)peer->sta->drv_priv; ++ arsta = ath11k_sta_to_arsta(peer->sta); + ath11k_dp_rx_update_peer_su_stats(arsta, ppdu_info); + ath11k_nss_update_sta_rxrate(ppdu_info, peer, NULL); + } else { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4455,7 +4455,7 @@ static int ath11k_mac_op_set_key(struct + mutex_lock(&ar->conf_mutex); + if (sta) { + peer_addr = sta->addr; +- arsta = (struct ath11k_sta *)sta->drv_priv; ++ arsta = ath11k_sta_to_arsta(sta); + } else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { + peer_addr = vif->bss_conf.bssid; + } else { diff --git a/package/kernel/mac80211/patches/nss/ath11k/999-904-ath11k-nss-use-wrapper-to-avoid-cast.patch b/package/kernel/mac80211/patches/nss/ath11k/999-904-ath11k-nss-use-wrapper-to-avoid-cast.patch new file mode 100644 index 00000000000000..02ba6dffebc099 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/ath11k/999-904-ath11k-nss-use-wrapper-to-avoid-cast.patch @@ -0,0 +1,270 @@ +--- a/drivers/net/wireless/ath/ath11k/nss.c ++++ b/drivers/net/wireless/ath/ath11k/nss.c +@@ -313,13 +313,14 @@ static void ath11k_nss_peer_mem_free(str + + /*-----------------------------Events/Callbacks------------------------------*/ + +-void ath11k_nss_wifili_event_receive(struct ath11k_base *ab, struct nss_wifili_msg *msg) ++void ath11k_nss_wifili_event_receive(void *context, 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; ++ struct ath11k_base *ab = (struct ath11k_base *)context; + + if (!ab) + return; +@@ -519,6 +520,17 @@ ath11k_nss_wifili_ext_callback_fn(struct + } + } + ++static inline void ath11k_nss_wifili_ext_callback_wrapper(struct net_device *netdev, ++ struct sk_buff *skb, ++ struct napi_struct *napi) ++{ ++ // Retrieve the ath11k_base from the net_device's private data ++ struct ath11k_base *ab = (struct ath11k_base *)netdev_priv(netdev); ++ ++ // Call the original function with the retrieved ath11k_base ++ ath11k_nss_wifili_ext_callback_fn(ab, skb, napi); ++} ++ + void ath11k_nss_vdev_cfg_cb(void *app_data, struct nss_cmn_msg *msg) + { + struct ath11k_vif *arvif = (struct ath11k_vif *)app_data; +@@ -1431,7 +1443,7 @@ int ath11k_nss_mesh_exception_flags(stru + nss_tx_status_t status; + int ret = 0; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1532,7 +1544,7 @@ static int ath11k_nss_mesh_mpath_add(str + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1578,7 +1590,7 @@ static int ath11k_nss_mesh_mpath_update( + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1634,7 +1646,7 @@ static int ath11k_nss_mesh_mpath_del(str + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1670,7 +1682,7 @@ static int ath11k_nss_mesh_mpp_add_cmd(s + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1706,7 +1718,7 @@ static int ath11k_nss_mesh_mpp_update_cm + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1744,7 +1756,7 @@ static int ath11k_nss_mesh_mpp_del_cmd(s + if (!msg) + return -ENOMEM; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = 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); +@@ -1933,7 +1945,7 @@ void ath11k_nss_mpp_timer_cb(struct time + LIST_HEAD(local_entry); + nss_tx_status_t status; + +- msg_cb = (nss_wifi_mesh_msg_callback_t)ath11k_nss_mesh_wifili_event_receive; ++ msg_cb = ath11k_nss_mesh_wifili_event_receive; + + if (!arvif->nss.mpp_dump_req) + arvif->nss.mpp_dump_num_entries = 0; +@@ -3067,7 +3079,7 @@ int ath11k_nss_ext_vdev_wds_4addr_allow( + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, + NSS_WIFILI_PEER_4ADDR_EVENT_MSG, +@@ -3472,7 +3484,7 @@ int ath11k_nss_set_peer_sec_type(struct + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, + NSS_WIFILI_PEER_SECURITY_TYPE_MSG, +@@ -3511,7 +3523,7 @@ int ath11k_nss_set_peer_authorize(struct + auth_msg->peer_id = peer_id; + auth_msg->auth_flag = 1; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, + NSS_WIFILI_PEER_UPDATE_AUTH_FLAG, +@@ -3743,7 +3755,7 @@ int ath11k_nss_peer_delete(struct ath11k + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ar->ab->nss.if_num, + NSS_WIFILI_PEER_DELETE_MSG, +@@ -3841,7 +3853,7 @@ int ath11k_nss_peer_create(struct ath11k + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_PEER_CREATE_MSG, +@@ -3909,7 +3921,7 @@ int ath11k_nss_add_wds_peer(struct ath11 + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_WDS_PEER_ADD_MSG, +@@ -3955,7 +3967,7 @@ int ath11k_nss_update_wds_peer(struct at + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_WDS_PEER_UPDATE_MSG, +@@ -4006,7 +4018,7 @@ int ath11k_nss_map_wds_peer(struct ath11 + + ether_addr_copy(wds_peer_map_msg->dest_mac, dest_mac); + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_WDS_PEER_MAP_MSG, +@@ -4052,7 +4064,7 @@ int ath11k_nss_del_wds_peer(struct ath11 + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_WDS_PEER_DEL_MSG, +@@ -4366,7 +4378,7 @@ static int ath11k_nss_mesh_capability(st + if (!wlmsg) + return -ENOMEM; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + reinit_completion(&ab->nss.complete); + +@@ -4483,7 +4495,7 @@ static int ath11k_nss_init(struct ath11k + + /* 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_callback_t)ath11k_nss_wifili_ext_callback_wrapper, + (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive, + (struct net_device *)ab, features); + +@@ -4502,7 +4514,7 @@ static int ath11k_nss_init(struct ath11k + */ + ab->nss.ctx = nss_contex; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + /* Initialize the common part of the wlmsg */ + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, +@@ -4574,7 +4586,7 @@ static int ath11k_nss_stats_cfg(struct a + stats_cfg = &wlmsg->msg.scm; + stats_cfg->cfg = enable; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + nss_msg, +@@ -4684,7 +4696,7 @@ int ath11k_nss_pdev_init(struct ath11k_b + 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; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_PDEV_INIT_MSG, +@@ -4758,7 +4770,7 @@ int ath11k_nss_start(struct ath11k_base + if (!wlmsg) + return -ENOMEM; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + /* Empty message for NSS Start message */ + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, +@@ -4816,7 +4828,7 @@ static void ath11k_nss_reset(struct ath1 + return; + } + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + /* Empty message for NSS Reset message */ + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, +@@ -4869,7 +4881,7 @@ static int ath11k_nss_stop(struct ath11k + if (!wlmsg) + return -ENOMEM; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + /* Empty message for Stop command */ + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, +@@ -4936,7 +4948,7 @@ int ath11k_nss_pdev_deinit(struct ath11k + deinit = &wlmsg->msg.pdevdeinit; + deinit->ifnum = radio_id; + +- msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive; ++ msg_cb = ath11k_nss_wifili_event_receive; + + nss_cmn_msg_init(&wlmsg->cm, ab->nss.if_num, + NSS_WIFILI_PDEV_DEINIT_MSG, 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..7d896ac65442aa --- /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 +@@ -4717,8 +4717,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..495b034687242b --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/199-001-mac80211-add-nss-support.patch @@ -0,0 +1,467 @@ +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/backport-include/linux/netdevice.h ++++ b/backport-include/linux/netdevice.h +@@ -4,6 +4,8 @@ + #include + #include + ++#define netdev_tstats(dev) dev->tstats ++ + #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) +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -406,6 +406,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 +@@ -740,6 +754,7 @@ struct ieee80211_parsed_tpe { + * @eht_80mhz_full_bw_ul_mumimo: in AP-mode, does this BSS support the + * reception of an EHT TB PPDU on an RU that spans the entire PPDU + * bandwidth ++ * @nss_ap_isolate: Used for notifying the NSS host about AP isolate feature + */ + struct ieee80211_bss_conf { + struct ieee80211_vif *vif; +@@ -834,6 +849,7 @@ struct ieee80211_bss_conf { + bool eht_su_beamformee; + bool eht_mu_beamformer; + bool eht_80mhz_full_bw_ul_mumimo; ++ bool nss_ap_isolate; + }; + + /** +@@ -1483,7 +1499,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 +@@ -1491,7 +1507,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) +@@ -2127,6 +2143,16 @@ static inline bool lockdep_vif_wiphy_mut + lockdep_vif_wiphy_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 +@@ -2830,6 +2856,8 @@ struct ieee80211_txq { + * implements MLO, so operation can continue on other links when one + * link is switching. + * ++ * @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 { +@@ -2890,6 +2918,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_DISALLOW_PUNCTURING, + IEEE80211_HW_DISALLOW_PUNCTURING_5GHZ, + IEEE80211_HW_HANDLES_QUIET_CSA, ++ IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +@@ -3905,6 +3934,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. +@@ -4467,7 +4500,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, +@@ -4777,7 +4812,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 +@@ -509,6 +509,7 @@ static const char *hw_flag_names[] = { + FLAG(DISALLOW_PUNCTURING), + FLAG(DISALLOW_PUNCTURING_5GHZ), + FLAG(HANDLES_QUIET_CSA), ++ FLAG(SUPPORTS_NSS_OFFLOAD), + #undef FLAG + }; + +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -875,6 +875,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 +@@ -416,6 +416,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; +@@ -854,12 +865,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); +@@ -1187,6 +1192,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 +@@ -2412,6 +2412,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 +@@ -1027,11 +1027,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; + } +@@ -2877,7 +2889,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); + +@@ -4667,7 +4681,8 @@ 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->status_data = ieee80211_store_ack_skb(local, skb, + &info->flags, NULL); + if (info->status_data) +@@ -4675,8 +4690,10 @@ static void ieee80211_8023_xmit(struct i + } + + 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 +@@ -2631,6 +2631,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 +@@ -2704,7 +2704,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)) +@@ -2754,6 +2754,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); + } + +@@ -2782,6 +2784,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 +@@ -180,6 +180,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 +@@ -1861,6 +1861,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 +@@ -421,6 +421,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 +@@ -986,7 +986,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 +@@ -115,6 +115,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 +@@ -59,6 +59,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_GENI_SE= 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..713ed77c750568 --- /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 +@@ -6262,7 +6262,13 @@ int ieee80211_tx_control_port(struct wip + + start_xmit: + 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(); + + return 0; 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..7800bad4423e04 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/203-mac80211-ath11k-fw-dynamic-muedca.patch @@ -0,0 +1,260 @@ +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 +@@ -9766,4 +9766,15 @@ ssize_t wiphy_locked_debugfs_write(struc + void *data); + #endif + ++/** ++ * 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 +@@ -7566,6 +7566,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 +@@ -1329,6 +1329,10 @@ + * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the + * TID to Link mapping for downlink/uplink traffic. + * ++ * @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 + */ +@@ -1586,6 +1590,7 @@ enum nl80211_commands { + + NL80211_CMD_SET_TID_TO_LINK_MAPPING, + ++ NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS, + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ +@@ -2871,6 +2876,8 @@ enum nl80211_commands { + * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32). + * A value of 0 means all radios. + * ++ * @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 +@@ -3421,6 +3428,8 @@ enum nl80211_attrs { + + NL80211_ATTR_VIF_RADIO_MASK, + ++ 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 +@@ -9341,3 +9341,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 +@@ -3134,6 +3134,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 ++ ) ++); ++ + TRACE_EVENT(drv_can_neg_ttlm, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -825,6 +825,7 @@ static const struct nla_policy nl80211_p + [NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG }, + [NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG }, ++ [NL80211_ATTR_HE_MUEDCA_PARAMS] = NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_mu_edca_param_set)), + [NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG }, + [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), + [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), +@@ -20442,6 +20443,42 @@ void cfg80211_schedule_channels_check(st + } + EXPORT_SYMBOL(cfg80211_schedule_channels_check); + ++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) +--- a/net/wireless/trace.h ++++ b/net/wireless/trace.h +@@ -3977,6 +3977,46 @@ TRACE_EVENT(cfg80211_update_owe_info_eve + __entry->assoc_link_id, __entry->peer_mld_addr) + ); + ++TRACE_EVENT(cfg80211_update_muedca_params_event, ++ TP_PROTO(struct wiphy *wiphy, ++ const struct ieee80211_mu_edca_param_set *params), ++ TP_ARGS(wiphy, params), ++ TP_STRUCT__entry( ++ WIPHY_ENTRY ++ __field(u8, mu_qos_info) ++ __field(u8, be_aifsn) ++ __field(u8, be_ecw_min_max) ++ __field(u8, be_mu_edca_timer) ++ __field(u8, bk_aifsn) ++ __field(u8, bk_ecw_min_max) ++ __field(u8, bk_mu_edca_timer) ++ __field(u8, vi_aifsn) ++ __field(u8, vi_ecw_min_max) ++ __field(u8, vi_mu_edca_timer) ++ __field(u8, vo_aifsn) ++ __field(u8, vo_ecw_min_max) ++ __field(u8, vo_mu_edca_timer) ++ ), ++ TP_fast_assign( ++ WIPHY_ASSIGN; ++ __entry->mu_qos_info = params->mu_qos_info; ++ __entry->be_aifsn = params->ac_be.aifsn; ++ __entry->be_ecw_min_max = params->ac_be.ecw_min_max; ++ __entry->be_mu_edca_timer = params->ac_be.mu_edca_timer; ++ __entry->bk_aifsn = params->ac_bk.aifsn; ++ __entry->bk_ecw_min_max = params->ac_bk.ecw_min_max; ++ __entry->bk_mu_edca_timer = params->ac_bk.mu_edca_timer; ++ __entry->vi_aifsn = params->ac_vi.aifsn; ++ __entry->vi_ecw_min_max = params->ac_vi.ecw_min_max; ++ __entry->vi_mu_edca_timer = params->ac_vi.mu_edca_timer; ++ __entry->vo_aifsn = params->ac_vo.aifsn; ++ __entry->vo_ecw_min_max = params->ac_vo.ecw_min_max; ++ __entry->vo_mu_edca_timer = params->ac_vo.mu_edca_timer; ++ ), ++ TP_printk(WIPHY_PR_FMT ", MU QOS info: %u", WIPHY_PR_ARG, ++ __entry->mu_qos_info) ++); ++ + TRACE_EVENT(cfg80211_bss_color_notify, + TP_PROTO(struct net_device *netdev, + enum nl80211_commands cmd, 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..18470318f1a68d --- /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, +@@ -4294,6 +4297,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); +@@ -4315,6 +4320,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..cf8ecba3c5de42 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/207-mac80211-add-nss-redirect-support.patch @@ -0,0 +1,276 @@ +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 +@@ -35,6 +35,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; + +@@ -1186,6 +1189,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 + * +@@ -776,6 +782,13 @@ static int ieee80211_stop(struct net_dev + ieee80211_stop_mbssid(sdata); + } + ++#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); + wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->activate_links_work); + +@@ -1227,6 +1240,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 +@@ -1457,6 +1498,21 @@ 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 +@@ -2613,6 +2613,60 @@ 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); ++ case_rtn_string(NSS_TX_FAILURE_NOT_ENABLED); ++ case_rtn_string(NSS_TX_FAILURE_SYNC_BAD_PARAM); ++ case_rtn_string(NSS_TX_FAILURE_SYNC_TIMEOUT); ++ case_rtn_string(NSS_TX_FAILURE_SYNC_FW_ERR); ++ 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()) { ++ if (NSS_TX_FAILURE_NOT_ENABLED == ret) ++ goto out; ++ 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) + { +@@ -2652,11 +2706,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 +@@ -4527,6 +4527,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 +@@ -4542,6 +4571,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; + +@@ -4731,6 +4764,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..b1ec5d17770af8 --- /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,221 @@ +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 +@@ -5276,6 +5276,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 +@@ -2217,7 +2217,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 +@@ -62,11 +62,10 @@ int drv_add_interface(struct ieee80211_l + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + +- 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) && + !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) && +- !(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 +@@ -697,6 +697,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) +@@ -977,6 +980,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: +@@ -1031,7 +1035,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; + +@@ -1365,10 +1370,17 @@ int ieee80211_do_open(struct wireless_de + (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE); + 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 +@@ -1690,6 +1690,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 +@@ -4321,8 +4321,11 @@ 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) +@@ -4714,7 +4717,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 +@@ -1669,6 +1669,9 @@ static void ieee80211_assign_chanctx(str + + lockdep_assert_wiphy(local->hw.wiphy); + ++ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) ++ return; ++ + conf = rcu_dereference_protected(link->conf->chanctx_conf, + lockdep_is_held(&local->hw.wiphy->mtx)); + if (conf) { +@@ -1877,7 +1880,8 @@ int ieee80211_reconfig(struct ieee80211_ + if (sdata->vif.type == NL80211_IFTYPE_MONITOR && + !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) + continue; +- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN || ++ ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) && + ieee80211_sdata_running(sdata)) { + res = drv_add_interface(local, sdata); + if (WARN_ON(res)) +@@ -1894,7 +1898,8 @@ int ieee80211_reconfig(struct ieee80211_ + if (sdata->vif.type == NL80211_IFTYPE_MONITOR && + !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) + continue; +- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && ++ if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN || ++ ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) && + ieee80211_sdata_running(sdata)) + drv_remove_interface(local, sdata); + } +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -5700,7 +5700,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 +@@ -619,7 +619,9 @@ static inline void drv_sta_rate_tbl_upda + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta) + { +- 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..4e16510932bd46 --- /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 +@@ -2227,6 +2227,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) +@@ -2246,6 +2248,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 +@@ -540,6 +540,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 +@@ -135,7 +135,11 @@ int drv_sta_state(struct ieee80211_local + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + +- 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 +@@ -474,7 +474,9 @@ static inline int drv_sta_add(struct iee + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + +- 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 +@@ -160,7 +160,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. +@@ -606,6 +607,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 +@@ -4673,16 +4673,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..d8f7350d9484cc --- /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 +@@ -218,6 +218,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 +@@ -4702,19 +4702,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)); +@@ -4774,7 +4776,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 +@@ -4792,9 +4794,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]); +@@ -4805,6 +4811,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; + +@@ -6329,13 +6336,7 @@ int ieee80211_tx_control_port(struct wip + + start_xmit: + 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(); + + return 0; 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..76743390fc087f --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/300-ath11k-nss-mesh-offload-support.patch @@ -0,0 +1,1245 @@ +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 + * +@@ -413,11 +416,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), + }; + + /* +@@ -847,6 +856,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 eht_80mhz_full_bw_ul_mumimo; + bool nss_ap_isolate; +@@ -1342,6 +1356,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; +@@ -1352,6 +1368,8 @@ struct ieee80211_tx_status { + u8 n_rates; + + struct list_head *free_list; ++ u32 mpdu_succ; ++ u32 mpdu_fail; + }; + + /** +@@ -1856,6 +1874,7 @@ struct ieee80211_channel_switch { + * operation on this interface and request a channel context without + * the AP definition. Use this e.g. because the device is able to + * handle OFDMA (downlink and trigger for uplink) on a per-AP basis. ++ * @IEEE80211_HW_NSS_OFFLOAD_DEBUG_MODE: It enables the debug mode of nss offload. + */ + enum ieee80211_vif_flags { + IEEE80211_VIF_BEACON_FILTER = BIT(0), +@@ -1864,6 +1883,7 @@ enum ieee80211_vif_flags { + IEEE80211_VIF_GET_NOA_UPDATE = BIT(3), + IEEE80211_VIF_EML_ACTIVE = BIT(4), + IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW = BIT(5), ++ IEEE80211_VIF_NSS_OFFLOAD_DEBUG_MODE = BIT(6), + }; + + +@@ -2922,6 +2942,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_DISALLOW_PUNCTURING_5GHZ, + IEEE80211_HW_HANDLES_QUIET_CSA, + IEEE80211_HW_SUPPORTS_NSS_OFFLOAD, ++ IEEE80211_HW_SUPPORTS_MESH_NSS_OFFLOAD, + + /* keep last, obviously */ + NUM_IEEE80211_HW_FLAGS +@@ -4430,6 +4451,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. +@@ -4824,6 +4847,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); +@@ -7731,4 +7760,100 @@ int ieee80211_emulate_switch_vif_chanctx + int n_vifs, + enum ieee80211_chanctx_switch_mode mode); + ++/* 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 +@@ -2547,6 +2547,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; +@@ -2563,8 +2564,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)) { +@@ -2578,8 +2582,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)) +@@ -2614,8 +2622,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. +@@ -2657,6 +2669,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 +@@ -216,6 +222,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 +@@ -510,6 +510,7 @@ static const char *hw_flag_names[] = { + FLAG(DISALLOW_PUNCTURING_5GHZ), + FLAG(HANDLES_QUIET_CSA), + FLAG(SUPPORTS_NSS_OFFLOAD), ++ FLAG(SUPPORTS_MESH_NSS_OFFLOAD), + #undef FLAG + }; + +--- a/net/mac80211/driver-ops.c ++++ b/net/mac80211/driver-ops.c +@@ -622,3 +622,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/mesh.h ++++ b/net/mac80211/mesh.h +@@ -321,6 +321,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); +@@ -362,6 +366,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 +@@ -367,6 +367,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 + * +@@ -390,9 +397,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; +@@ -494,7 +502,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; + } +@@ -516,6 +527,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); + } +@@ -546,7 +559,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; + } +@@ -563,6 +583,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); + } +@@ -699,15 +721,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) +@@ -1351,3 +1364,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 +@@ -16,6 +16,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); + +@@ -104,6 +105,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 + * +@@ -241,16 +299,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; + } +@@ -267,13 +332,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 * +@@ -337,6 +402,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(); +@@ -349,6 +415,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); +@@ -357,6 +424,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); +@@ -373,16 +442,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); +@@ -668,17 +742,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; +@@ -719,8 +784,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; +@@ -758,6 +851,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 +@@ -808,8 +920,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) +@@ -817,6 +950,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); + } + + /** +@@ -840,7 +978,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); + } +@@ -855,19 +993,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; + } +@@ -1046,7 +1184,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; +@@ -1060,6 +1201,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) +@@ -1071,7 +1214,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; +@@ -1081,15 +1224,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 +@@ -2629,7 +2629,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); + +@@ -2641,6 +2641,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]; +@@ -2711,6 +2714,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; +@@ -2974,6 +2984,9 @@ static struct sk_buff *ieee80211_build_h + + skb_reset_mac_header(skb); + ++#ifdef CPTCFG_MAC80211_MESH ++nss_mesh: ++#endif + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + +@@ -4298,6 +4311,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)) { +@@ -4372,9 +4386,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: +--- a/net/mac80211/driver-ops.h ++++ b/net/mac80211/driver-ops.h +@@ -1749,4 +1749,10 @@ drv_can_neg_ttlm(struct ieee80211_local + + return res; + } ++#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 */ 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..49ab2a064eaa00 --- /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 +@@ -2881,6 +2881,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 { +@@ -2943,6 +2945,7 @@ enum ieee80211_hw_flags { + IEEE80211_HW_HANDLES_QUIET_CSA, + 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 +@@ -511,6 +511,7 @@ static const char *hw_flag_names[] = { + FLAG(HANDLES_QUIET_CSA), + 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..603b8cd67c18da --- /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 +@@ -4791,6 +4791,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->status_data = 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) + { +@@ -4830,6 +4891,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..86543781338107 --- /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 +@@ -1770,6 +1770,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: +@@ -1784,6 +1786,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 +@@ -389,6 +389,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; + } + +@@ -856,6 +858,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; + } + +@@ -874,7 +877,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]); +@@ -934,6 +937,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; +@@ -949,6 +953,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: +@@ -995,6 +1000,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 +@@ -4724,10 +4724,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; +@@ -4768,7 +4773,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; +@@ -4950,6 +4955,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 +@@ -4999,6 +5008,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))) +@@ -5036,9 +5076,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..bdaf7f1884f3ad --- /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 +@@ -4722,7 +4722,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) { +@@ -4776,7 +4776,7 @@ static void ieee80211_8023_xmit(struct i + } + + 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/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..8579a4c10d33d5 --- /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 +@@ -1015,7 +1015,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..1a22f5a77977d4 --- /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,74 @@ +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -1538,39 +1538,38 @@ ieee80211_tx_info_clear_status(struct ie + * hardware or driver) + */ + enum mac80211_rx_flags { +- RX_FLAG_MMIC_ERROR = BIT(0), +- RX_FLAG_DECRYPTED = BIT(1), +- RX_FLAG_ONLY_MONITOR = BIT(2), +- RX_FLAG_MMIC_STRIPPED = BIT(3), +- RX_FLAG_IV_STRIPPED = BIT(4), +- RX_FLAG_FAILED_FCS_CRC = BIT(5), +- RX_FLAG_FAILED_PLCP_CRC = BIT(6), +- RX_FLAG_MACTIME_IS_RTAP_TS64 = BIT(7), +- RX_FLAG_NO_SIGNAL_VAL = BIT(8), +- RX_FLAG_AMPDU_DETAILS = BIT(9), +- RX_FLAG_PN_VALIDATED = BIT(10), +- RX_FLAG_DUP_VALIDATED = BIT(11), +- RX_FLAG_AMPDU_LAST_KNOWN = BIT(12), +- RX_FLAG_AMPDU_IS_LAST = BIT(13), +- RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14), +- /* one free bit at 15 */ +- RX_FLAG_MACTIME = BIT(16) | BIT(17), +- RX_FLAG_MACTIME_PLCP_START = 1 << 16, +- RX_FLAG_MACTIME_START = 2 << 16, +- RX_FLAG_MACTIME_END = 3 << 16, +- 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_MMIC_ERROR = BIT(0), ++ RX_FLAG_DECRYPTED = BIT(1), ++ RX_FLAG_ONLY_MONITOR = BIT(2), ++ RX_FLAG_MMIC_STRIPPED = BIT(3), ++ RX_FLAG_IV_STRIPPED = BIT(4), ++ RX_FLAG_FAILED_FCS_CRC = BIT(5), ++ RX_FLAG_FAILED_PLCP_CRC = BIT(6), ++ RX_FLAG_MACTIME_IS_RTAP_TS64 = BIT(7), ++ RX_FLAG_NO_SIGNAL_VAL = BIT(8), ++ RX_FLAG_AMPDU_DETAILS = BIT(9), ++ RX_FLAG_PN_VALIDATED = BIT(10), ++ RX_FLAG_DUP_VALIDATED = BIT(11), ++ RX_FLAG_AMPDU_LAST_KNOWN = BIT(12), ++ RX_FLAG_AMPDU_IS_LAST = BIT(13), ++ RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14), ++ RX_FLAG_MACTIME = BIT(15) | BIT(16), ++ RX_FLAG_MACTIME_PLCP_START = 1 << 15, ++ RX_FLAG_MACTIME_START = 2 << 15, ++ RX_FLAG_MACTIME_END = 3 << 15, ++ 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), + }; + + /** 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..4e4e7885108bfa --- /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 +@@ -2083,6 +2083,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, +@@ -4344,7 +4345,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; + } +@@ -4390,7 +4391,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); +@@ -4690,7 +4691,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; +@@ -4746,6 +4748,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 && +@@ -4766,11 +4769,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->status_data = ieee80211_store_ack_skb(local, skb, +- &info->flags, NULL); ++ &info->flags, cookie); + if (info->status_data) + info->status_data_idr = 1; + } +@@ -4793,7 +4797,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; +@@ -4802,6 +4807,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. +@@ -4813,11 +4821,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->status_data = 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; +@@ -4851,14 +4861,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); +@@ -4874,14 +4893,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]); +@@ -4892,13 +4912,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: +@@ -6422,7 +6442,10 @@ int ieee80211_tx_control_port(struct wip + + start_xmit: + 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(); + + return 0; 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..fbae3e18769dd1 --- /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 +@@ -2374,7 +2374,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 +@@ -127,7 +127,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 +@@ -506,7 +506,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); +@@ -567,7 +567,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..4170336f3dad67 --- /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 +@@ -2407,7 +2407,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; +@@ -2420,8 +2420,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; +@@ -2498,7 +2503,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; +@@ -2598,7 +2603,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; + +@@ -2882,7 +2887,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..d2a558ad2f7458 --- /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, +@@ -3657,7 +3659,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; +@@ -3675,7 +3677,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) +@@ -3821,7 +3823,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; + +@@ -4368,7 +4370,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; +@@ -4741,7 +4743,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 +@@ -2270,6 +2270,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; + +@@ -779,7 +781,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..8e6e9ca32ed64b --- /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 +@@ -2882,6 +2882,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 { +@@ -2945,6 +2948,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 +@@ -512,6 +512,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 +@@ -1601,6 +1601,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; +@@ -1648,6 +1651,9 @@ void ieee80211_txq_teardown_flows(struct + { + struct fq *fq = &local->fq; + ++ if (ieee80211_hw_check(&local->hw, HAS_TX_QUEUE)) ++ return; ++ + kvfree(local->cvars); + local->cvars = NULL; + +@@ -1664,7 +1670,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) +@@ -4353,7 +4360,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) { +@@ -4707,8 +4715,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); + +@@ -6429,9 +6439,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 +@@ -4597,6 +4597,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 +@@ -1003,7 +1003,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 +@@ -1588,6 +1588,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 +@@ -118,15 +118,17 @@ void ieee80211_purge_sta_txqs(struct sta + struct ieee80211_local *local = sta->sdata->local; + int i; + +- 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); ++ } + } + } + +@@ -436,7 +438,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); +@@ -537,8 +541,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 +@@ -1715,11 +1722,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); +@@ -2134,6 +2143,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)) || +@@ -2568,7 +2580,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(); + +@@ -2897,6 +2909,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 +@@ -814,7 +814,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 +@@ -470,10 +470,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..6ae63db080448c --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/829-mac80211-fix-mesh-ping-issue.patch @@ -0,0 +1,112 @@ +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 +@@ -4727,16 +4727,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; ++ fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3); ++ fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr4); + break; ++ + default: + goto clear; + } +@@ -4958,10 +4956,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 +@@ -5011,37 +5006,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))) +@@ -5079,30 +5043,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..a070e2c4b5fbab --- /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 +@@ -731,7 +731,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 +@@ -1168,7 +1168,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); + } +@@ -1250,7 +1250,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); +@@ -1719,9 +1719,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/mac80211/patches/nss/subsys/917-QSDK-mac80211-WAR-for-association-failure-issue-with.patch b/package/kernel/mac80211/patches/nss/subsys/917-QSDK-mac80211-WAR-for-association-failure-issue-with.patch new file mode 100644 index 00000000000000..c0e43a73c34878 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/917-QSDK-mac80211-WAR-for-association-failure-issue-with.patch @@ -0,0 +1,38 @@ +From 3bce46935954d8439e980a2188a6befbb42a3803 Mon Sep 17 00:00:00 2001 +From: Balamurugan Ramar +Date: Wed, 8 May 2024 23:01:34 +0530 +Subject: [PATCH] QSDK: mac80211: WAR for association failure issue with WDS + +Every 100 milliseconds, the station sends an "association request" and +the AP adds the peer and the AP responds to the station with an +"association response". + +But "association response" takes time to reach the station. Because of +this, the station sends another "association request" to the AP. + +In the second associate request, the AP removes the peer and re-adds +the station. Meanwhile, "association response" is reached to station +and station sends "QoS NULL" frame to AP. + +The moment the station sends a QoS NULL frame to the ap, the peer on +the AP is deleted and the AP sends a de-authentication to the station. + +To fix this issue, increased the assoc timeout period from 100 milli- +seconds to 500 milli seconds. + +Signed-off-by: Balamurugan Ramar +--- + net/mac80211/mlme.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -7757,7 +7757,7 @@ void ieee80211_sta_work(struct ieee80211 + */ + if (status_acked) { + ifmgd->assoc_data->timeout = +- jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; ++ jiffies + IEEE80211_ASSOC_TIMEOUT_LONG; + run_again(sdata, ifmgd->assoc_data->timeout); + } else { + ifmgd->assoc_data->timeout = jiffies - 1; diff --git a/package/kernel/mac80211/patches/nss/subsys/970-mac80211-ath10k-fix-airtime-underflow.patch b/package/kernel/mac80211/patches/nss/subsys/970-mac80211-ath10k-fix-airtime-underflow.patch new file mode 100644 index 00000000000000..ce79cdad0c57d1 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/970-mac80211-ath10k-fix-airtime-underflow.patch @@ -0,0 +1,14 @@ +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -2408,10 +2408,7 @@ void ieee80211_sta_update_pending_airtim + atomic_sub(tx_airtime, &local->aql_total_pending_airtime); + tx_pending = atomic_sub_return(tx_airtime, + &local->aql_ac_pending_airtime[ac]); +- if (WARN_ONCE(tx_pending < 0, +- "Device %s AC %d pending airtime underflow: %u, %u", +- wiphy_name(local->hw.wiphy), ac, tx_pending, +- tx_airtime)) { ++ if (tx_pending < 0) { + atomic_cmpxchg(&local->aql_ac_pending_airtime[ac], + tx_pending, 0); + atomic_sub(tx_pending, &local->aql_total_pending_airtime); diff --git a/package/kernel/mac80211/patches/nss/subsys/999-775-wifi-mac80211-Changes-for-WDS-MLD.patch b/package/kernel/mac80211/patches/nss/subsys/999-775-wifi-mac80211-Changes-for-WDS-MLD.patch new file mode 100644 index 00000000000000..312484631694e0 --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/999-775-wifi-mac80211-Changes-for-WDS-MLD.patch @@ -0,0 +1,210 @@ +From abd9e23af3835b9d21b9e0b9d885142dff73d741 Mon Sep 17 00:00:00 2001 +From: Ramasamy Kaliappan +Date: Wed, 17 May 2023 16:26:23 +0530 +Subject: [PATCH] cfg80211/mac80211: Changes for WDS MLD + +Add link id attributes for NL80211_CMD_UNEXPECTED_4ADDR_FRAME. + +Based on the station's link, add links to the vlan interface + +Signed-off-by: Ramasamy Kaliappan +--- + include/net/cfg80211.h | 4 +++- + net/mac80211/cfg.c | 37 +++++++++++++++++++++++++++++++++++++ + net/mac80211/rx.c | 8 +++++--- + net/wireless/nl80211.c | 13 +++++++++---- + 4 files changed, 54 insertions(+), 8 deletions(-) + +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -8871,6 +8871,7 @@ bool cfg80211_rx_spurious_frame(struct n + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags ++ * @link_id: link id + * + * This function is used in AP mode (only!) to inform userspace that + * an associated station sent a 4addr frame but that wasn't expected. +@@ -8880,7 +8881,8 @@ bool cfg80211_rx_spurious_frame(struct n + * for a reason other than not having a subscription.) + */ + bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, +- const u8 *addr, gfp_t gfp); ++ const u8 *addr, gfp_t gfp, ++ const int link_id); + + /** + * cfg80211_probe_status - notify userspace about probe status +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -2216,9 +2216,16 @@ static int ieee80211_change_station(stru + vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); + + if (params->vlan->ieee80211_ptr->use_4addr) { ++ struct ieee80211_sub_if_data *master; ++ struct wireless_dev *wdev; ++ + if (vlansdata->u.vlan.sta) + return -EBUSY; + ++ wdev = &vlansdata->wdev; ++ master = container_of(vlansdata->bss, ++ struct ieee80211_sub_if_data, u.ap); ++ + rcu_assign_pointer(vlansdata->u.vlan.sta, sta); + __ieee80211_check_fast_rx_iface(vlansdata); + +@@ -2228,6 +2235,39 @@ static int ieee80211_change_station(stru + else + drv_sta_set_4addr(local, sta->sdata, &sta->sta, + true); ++ if (sta->sta.valid_links) { ++ int link_id; ++ unsigned long valid_links = master->vif.valid_links; ++ for_each_set_bit(link_id, ++ &valid_links, ++ IEEE80211_MLD_MAX_NUM_LINKS) { ++ if (!(sta->sta.valid_links & BIT(link_id))) { ++ rcu_assign_pointer( ++ vlansdata->vif.link_conf[link_id], ++ NULL); ++ rcu_assign_pointer( ++ vlansdata->link[link_id], ++ NULL); ++ memset(wdev->links[link_id].addr, ++ 0, ETH_ALEN); ++ vlansdata->vif.valid_links &= ++ ~BIT(link_id); ++ wdev->valid_links &= ~BIT(link_id); ++ } ++ else { ++ rcu_assign_pointer( ++ vlansdata->vif.link_conf[link_id], ++ master->vif.link_conf[link_id]); ++ rcu_assign_pointer(vlansdata->link[link_id], ++ master->link[link_id]); ++ memcpy(wdev->links[link_id].addr, ++ vlansdata->vif.link_conf[link_id]->bssid, ++ ETH_ALEN); ++ vlansdata->vif.valid_links |= BIT(link_id); ++ wdev->valid_links |= BIT(link_id); ++ } ++ } ++ } + } + + if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -1695,7 +1695,7 @@ EXPORT_SYMBOL(ieee80211_sta_uapsd_trigge + + void ieee80211_rx_nss_notify_4addr(struct net_device *dev, u8 *sta_addr) + { +- cfg80211_rx_unexpected_4addr_frame(dev, sta_addr, GFP_ATOMIC); ++ cfg80211_rx_unexpected_4addr_frame(dev, sta_addr, GFP_ATOMIC, -1); + } + EXPORT_SYMBOL(ieee80211_rx_nss_notify_4addr); + +@@ -1870,7 +1870,8 @@ ieee80211_rx_h_sta_process(struct ieee80 + if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( + rx->sdata->dev, sta->sta.addr, +- GFP_ATOMIC); ++ GFP_ATOMIC, ++ rx->link_id); + return RX_DROP_M_UNEXPECTED_4ADDR_FRAME; + } + /* +@@ -3257,7 +3258,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_ + if (rx->sta && + !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( +- rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); ++ rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC, ++ rx->link_id); + return RX_DROP_MONITOR; + } + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -18991,7 +18991,8 @@ void cfg80211_conn_failed(struct net_dev + EXPORT_SYMBOL(cfg80211_conn_failed); + + static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, +- const u8 *addr, gfp_t gfp) ++ const u8 *addr, gfp_t gfp, ++ const int link_id) + { + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); +@@ -19017,6 +19018,9 @@ static bool __nl80211_unexpected_frame(s + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto nla_put_failure; + ++ if (link_id != -1) ++ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id); ++ + genlmsg_end(msg, hdr); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); + return true; +@@ -19040,14 +19044,15 @@ bool cfg80211_rx_spurious_frame(struct n + return false; + } + ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, +- addr, gfp); ++ addr, gfp, -1); + trace_cfg80211_return_bool(ret); + return ret; + } + EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + + bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, +- const u8 *addr, gfp_t gfp) ++ const u8 *addr, gfp_t gfp, ++ const int link_id) + { + struct wireless_dev *wdev = dev->ieee80211_ptr; + bool ret; +@@ -19062,7 +19067,7 @@ bool cfg80211_rx_unexpected_4addr_frame( + } + ret = __nl80211_unexpected_frame(dev, + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, +- addr, gfp); ++ addr, gfp, link_id); + trace_cfg80211_return_bool(ret); + return ret; + } +--- a/net/mac80211/chan.c ++++ b/net/mac80211/chan.c +@@ -1048,6 +1048,8 @@ __ieee80211_link_copy_chanctx_to_vlans(s + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { + struct ieee80211_bss_conf *vlan_conf; + ++ if (!(vlan->vif.valid_links & BIT(link_id))) ++ continue; + vlan_conf = wiphy_dereference(local->hw.wiphy, + vlan->vif.link_conf[link_id]); + if (WARN_ON(!vlan_conf)) +@@ -1294,6 +1296,8 @@ ieee80211_link_update_chanreq(struct iee + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { + struct ieee80211_bss_conf *vlan_conf; + ++ if (!(vlan->vif.valid_links & BIT(link_id))) ++ continue; + vlan_conf = wiphy_dereference(sdata->local->hw.wiphy, + vlan->vif.link_conf[link_id]); + if (WARN_ON(!vlan_conf)) +--- a/net/mac80211/mlme.c ++++ b/net/mac80211/mlme.c +@@ -2062,9 +2062,9 @@ void ieee80211_send_4addr_nullfunc(struc + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); + nullfunc->frame_control = fc; +- memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); ++ memcpy(nullfunc->addr1, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); +- memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN); ++ memcpy(nullfunc->addr3, sdata->vif.cfg.ap_addr, ETH_ALEN); + memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); + + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; diff --git a/package/kernel/mac80211/patches/nss/subsys/999-918-mac80211-fix-NULL-pointer-access-Klocwork-issue.patch b/package/kernel/mac80211/patches/nss/subsys/999-918-mac80211-fix-NULL-pointer-access-Klocwork-issue.patch new file mode 100644 index 00000000000000..5444178b10b5ad --- /dev/null +++ b/package/kernel/mac80211/patches/nss/subsys/999-918-mac80211-fix-NULL-pointer-access-Klocwork-issue.patch @@ -0,0 +1,62 @@ +From f5a907934f596f77b4c59e549eff5d56b1a4e226 Mon Sep 17 00:00:00 2001 +From: Monika Korada +Date: Mon, 27 May 2024 11:32:49 +0530 +Subject: [PATCH] KW: wifi: mac80211: fix NULL pointer access, Klocwork issue + +Currently we are dereferencing dev pointer without a NULL +check. + +Fix this issue by adding a NULL check. + +Patch-dependency: none +Patch-work: none +Fixes: 4b7afb52c8e2 (mac80211: reorganize code to remove a forward declaration) +Note: ath.git commit id is mentioned in the Fixes tag + +Signed-off-by: Monika Korada +--- + net/mac80211/iface.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -1373,7 +1373,8 @@ int ieee80211_do_open(struct wireless_de + case NL80211_IFTYPE_AP_VLAN: + if (sdata->bss->active) { + ieee80211_link_vlan_copy_chanctx(&sdata->deflink); +- netif_carrier_on(dev); ++ if (dev) ++ netif_carrier_on(dev); + + if (ieee80211_hw_check(&local->hw, SUPPORTS_NSS_OFFLOAD)) { + ieee80211_set_sdata_offload_flags(sdata); +@@ -1384,7 +1385,8 @@ int ieee80211_do_open(struct wireless_de + + ieee80211_set_vif_encap_ops(sdata); + } else { +- netif_carrier_off(dev); ++ if (dev) ++ netif_carrier_off(dev); + } + break; + case NL80211_IFTYPE_MONITOR: +@@ -1416,7 +1418,8 @@ int ieee80211_do_open(struct wireless_de + ieee80211_recalc_offload(local); + ieee80211_recalc_idle(local); + +- netif_carrier_on(dev); ++ if (dev) ++ netif_carrier_on(dev); + break; + default: + if (coming_up) { +@@ -1460,7 +1463,8 @@ int ieee80211_do_open(struct wireless_de + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: +- netif_carrier_off(dev); ++ if (dev) ++ netif_carrier_off(dev); + break; + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_NAN: diff --git a/package/kernel/nat46/Makefile b/package/kernel/nat46/Makefile index 296ef5a058e83e..bcd0cbd9f6a2f0 100644 --- a/package/kernel/nat46/Makefile +++ b/package/kernel/nat46/Makefile @@ -3,15 +3,17 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=nat46 -PKG_MIRROR_HASH:=09b93f31d10030d3b4f326066b544b70b1f60236d0482f27c384ed93b298c0a6 PKG_SOURCE_URL:=https://github.com/ayourtch/nat46.git -PKG_SOURCE_DATE:=2022-09-19 +PKG_SOURCE_DATE:=2024-08-12 PKG_SOURCE_PROTO:=git -PKG_SOURCE_VERSION:=4c5beee236841724219598fabb1edc93d4f08ce5 +PKG_SOURCE_VERSION:=b42c37d5c6dee4593df0fc60b37bc7ec4ee243a4 +PKG_MIRROR_HASH:=2d7bbb6cc22808f7e81d716e6bd7a27d9978fb802496304851946627e21534a5 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..072d0cc44715fc --- /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; +@@ -160,6 +174,11 @@ int nat46_netdev_create(struct net *net, + 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: +@@ -176,10 +195,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 net * + 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..af7f2c770b2902 --- /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(struct net *net, char * + 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..bad08339e6afcf --- /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 +@@ -59,12 +59,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..5f10ca10bb67bd --- /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(struct net *net, 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/network/services/hostapd/patches/900-hostapd-update-muedca-params.patch b/package/network/services/hostapd/patches/900-hostapd-update-muedca-params.patch new file mode 100644 index 00000000000000..684122d5be11b0 --- /dev/null +++ b/package/network/services/hostapd/patches/900-hostapd-update-muedca-params.patch @@ -0,0 +1,299 @@ +--- a/hostapd/ctrl_iface.c ++++ b/hostapd/ctrl_iface.c +@@ -1314,6 +1314,11 @@ static int hostapd_ctrl_iface_set(struct + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; ++ /* Incrementing MU-EDCA Parameter Set Update Count*/ ++ hapd->iface->conf->he_mu_edca.he_qos_info = ++ (hapd->iface->conf->he_mu_edca.he_qos_info & 0xf0) | ++ ((hapd->iface->conf->he_mu_edca.he_qos_info + 1) & ++ 0xf); + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); +--- a/src/ap/drv_callbacks.c ++++ b/src/ap/drv_callbacks.c +@@ -21,6 +21,7 @@ + #include "common/nan_de.h" + #include "crypto/random.h" + #include "p2p/p2p.h" ++#include "wpa_debug.h" + #include "wps/wps.h" + #include "fst/fst.h" + #include "wnm_ap.h" +@@ -2316,6 +2317,47 @@ static void hostapd_event_wds_sta_interf + ifname, MAC2STR(addr)); + } + ++static void hostapd_event_update_muedca_params(struct hostapd_data *hapd, ++ struct update_muedca *params) ++{ ++ int i; ++ u8 updated_count; ++ ++ /* Update current MU-EDCA parameters */ ++ for (i = 0; i < 3; i++) { ++ hapd->iface->conf->he_mu_edca.he_mu_ac_be_param[i] = ++ params->he_mu_ac_be_param[i]; ++ hapd->iface->conf->he_mu_edca.he_mu_ac_bk_param[i] = ++ params->he_mu_ac_bk_param[i]; ++ hapd->iface->conf->he_mu_edca.he_mu_ac_vo_param[i] = ++ params->he_mu_ac_vo_param[i]; ++ hapd->iface->conf->he_mu_edca.he_mu_ac_vi_param[i] = ++ params->he_mu_ac_vi_param[i]; ++ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, ++ HOSTAPD_LEVEL_DEBUG, ++ "MU-EDCA: Updated MU-EDCA parameters for AC %d: " ++ "BE: %d, BK: %d, VI: %d, VO: %d", ++ i, params->he_mu_ac_be_param[i], ++ params->he_mu_ac_bk_param[i], ++ params->he_mu_ac_vi_param[i], ++ params->he_mu_ac_vo_param[i]); ++ } ++ ++ /* Increment Parameter Set Update Count for MU-EDCA and WME EDCA only ++ * if any STA is connected ++ */ ++ if (hapd->num_sta) { ++ updated_count = (hapd->iface->conf->he_mu_edca.he_qos_info + 1) & 0xf; ++ hapd->iface->conf->he_mu_edca.he_qos_info &= 0xf0; ++ hapd->iface->conf->he_mu_edca.he_qos_info |= updated_count; ++ hapd->parameter_set_count++; ++ } ++ ++ /* Update beacon with updated MU-EDCA parameters */ ++ if (ieee802_11_update_beacons(hapd->iface)) ++ wpa_printf(MSG_WARNING, ++ "Failed to update beacons with MU-EDCA parameters"); ++} + + #ifdef CONFIG_OWE + static int hostapd_notif_update_dh_ie(struct hostapd_data *hapd, +@@ -2874,6 +2916,9 @@ void hostapd_wpa_event(void *ctx, enum w + hapd->conf->iface); + hostapd_event_color_change(hapd, true); + break; ++ case EVENT_UPDATE_MUEDCA_PARAMS: ++ hostapd_event_update_muedca_params(hapd, &data->update_muedca); ++ break; + #endif /* CONFIG_IEEE80211AX */ + #ifdef CONFIG_IEEE80211BE + case EVENT_MLD_INTERFACE_FREED: +--- a/src/ap/hostapd.c ++++ b/src/ap/hostapd.c +@@ -4511,6 +4511,10 @@ static int hostapd_fill_csa_settings(str + hapd->cs_count = settings->cs_count; + hapd->cs_block_tx = settings->block_tx; + ++ /* reset MU-EDCA and WME EDCA parameter set count */ ++ hapd->iface->conf->he_mu_edca.he_qos_info &= 0xfff0; ++ hapd->parameter_set_count = 0; ++ + ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa); + if (ret) { + free_beacon_data(&settings->beacon_after); +--- a/src/ap/ieee802_11_he.c ++++ b/src/ap/ieee802_11_he.c +@@ -19,6 +19,7 @@ + #include "sta_info.h" + #include "ieee802_11.h" + #include "dfs.h" ++#include "wmm.h" + + static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) + { +@@ -291,9 +292,16 @@ u8 * hostapd_eid_he_operation(struct hos + u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid) + { + struct ieee80211_he_mu_edca_parameter_set *edca; ++ struct hostapd_wmm_ac_params wmmp[WMM_AC_NUM]; + u8 *pos; + size_t i; + ++ /* Updating WME Parameter Set Count to avoid mismatch */ ++ os_memset(wmmp, 0, sizeof(wmmp)); ++ ++ if (hapd->conf->wmm_enabled) ++ wmm_calc_regulatory_limit(hapd, wmmp); ++ + pos = (u8 *) &hapd->iface->conf->he_mu_edca; + for (i = 0; i < sizeof(*edca); i++) { + if (pos[i]) +--- a/src/ap/wmm.c ++++ b/src/ap/wmm.c +@@ -61,8 +61,8 @@ wmm_set_regulatory_limit(const struct ho + /* + * Calculate WMM regulatory limit if any. + */ +-static void wmm_calc_regulatory_limit(struct hostapd_data *hapd, +- struct hostapd_wmm_ac_params *acp) ++void wmm_calc_regulatory_limit(struct hostapd_data *hapd, ++ struct hostapd_wmm_ac_params *acp) + { + struct hostapd_hw_modes *mode = hapd->iface->current_mode; + int c; +@@ -91,6 +91,10 @@ static void wmm_calc_regulatory_limit(st + os_memcpy(hapd->iface->prev_wmm, acp, + sizeof(hapd->iconf->wmm_ac_params)); + hapd->parameter_set_count++; ++ /* Incrementing MU-EDCA Parameter Set Update Count*/ ++ hapd->iface->conf->he_mu_edca.he_qos_info = ++ (hapd->iface->conf->he_mu_edca.he_qos_info & 0xf0) | ++ ((hapd->iface->conf->he_mu_edca.he_qos_info + 1) & 0xf); + } + } + +--- a/src/ap/wmm.h ++++ b/src/ap/wmm.h +@@ -13,6 +13,8 @@ + struct ieee80211_mgmt; + struct wmm_tspec_element; + ++void wmm_calc_regulatory_limit(struct hostapd_data *hapd, ++ struct hostapd_wmm_ac_params *acp); + u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); + int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, + size_t len); +--- a/src/drivers/driver.h ++++ b/src/drivers/driver.h +@@ -5980,6 +5980,16 @@ enum wpa_event_type { + EVENT_LINK_CH_SWITCH_STARTED, + + /** ++ * EVENT_UPDATE_MUEDCA_PARAMS - Updated MU-EDCA parameters received ++ * ++ * this event is emitted when updated MU-EDCA parameters from driver ++ * are received. updated MU-EDCA parameters need to be updated in ++ * beacon. ++ */ ++ EVENT_UPDATE_MUEDCA_PARAMS, ++ ++ ++ /** + * EVENT_TID_LINK_MAP - MLD event to set TID-to-link mapping + * + * This event is used by the driver to indicate the received TID-to-link +@@ -6963,6 +6973,16 @@ union wpa_event_data { + struct pasn_auth pasn_auth; + + /** ++ * struct update_muedca - Data for EVENT_UPDATE_MU_EDCA_PARAMS ++ */ ++ struct update_muedca { ++ u8 he_mu_ac_be_param[3]; ++ u8 he_mu_ac_bk_param[3]; ++ u8 he_mu_ac_vi_param[3]; ++ u8 he_mu_ac_vo_param[3]; ++ } update_muedca; ++ ++ /** + * struct port_authorized - Data for EVENT_PORT_AUTHORIZED + * @td_bitmap: For STA mode, transition disable bitmap, if received in + * EAPOL-Key msg 3/4 +--- a/src/drivers/driver_common.c ++++ b/src/drivers/driver_common.c +@@ -98,6 +98,7 @@ const char * event_to_string(enum wpa_ev + E2S(PASN_AUTH); + E2S(LINK_CH_SWITCH); + E2S(LINK_CH_SWITCH_STARTED); ++ E2S(UPDATE_MUEDCA_PARAMS); + E2S(TID_LINK_MAP); + E2S(LINK_RECONFIG); + E2S(MLD_INTERFACE_FREED); +--- a/src/drivers/driver_nl80211_event.c ++++ b/src/drivers/driver_nl80211_event.c +@@ -184,6 +184,7 @@ static const char * nl80211_command_to_s + C2S(NL80211_CMD_ADD_LINK_STA) + C2S(NL80211_CMD_MODIFY_LINK_STA) + C2S(NL80211_CMD_REMOVE_LINK_STA) ++ C2S(NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS) + C2S(NL80211_CMD_SET_HW_TIMESTAMP) + C2S(NL80211_CMD_LINKS_REMOVED) + C2S(NL80211_CMD_SET_TID_TO_LINK_MAPPING) +@@ -3650,6 +3651,35 @@ static void nl80211_port_authorized(stru + wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, &event); + } + ++static void nl80211_update_muedca_params_event(struct wpa_driver_nl80211_data *drv, ++ struct nlattr **tb) ++{ ++ struct host_update_muedca { ++ u8 mu_qos_info; ++ u8 ac_be[3]; ++ u8 ac_bk[3]; ++ u8 ac_vi[3]; ++ u8 ac_vo[3]; ++ }; ++ ++ struct host_update_muedca *rx_muedca_params; ++ union wpa_event_data ed; ++ int i; ++ ++ if (!tb[NL80211_ATTR_HE_MUEDCA_PARAMS]) ++ return; ++ ++ rx_muedca_params = nla_data(tb[NL80211_ATTR_HE_MUEDCA_PARAMS]); ++ ++ for (i = 0; i< 3; i++) { ++ ed.update_muedca.he_mu_ac_be_param[i] = rx_muedca_params->ac_be[i]; ++ ed.update_muedca.he_mu_ac_bk_param[i] = rx_muedca_params->ac_bk[i]; ++ ed.update_muedca.he_mu_ac_vi_param[i] = rx_muedca_params->ac_vi[i]; ++ ed.update_muedca.he_mu_ac_vo_param[i] = rx_muedca_params->ac_vo[i]; ++ } ++ ++ wpa_supplicant_event(drv->ctx, EVENT_UPDATE_MUEDCA_PARAMS, &ed); ++} + + static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +@@ -4177,6 +4207,9 @@ static void do_process_drv_event(struct + case NL80211_CMD_LINKS_REMOVED: + wpa_supplicant_event(drv->ctx, EVENT_LINK_RECONFIG, NULL); + break; ++ case NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS: ++ nl80211_update_muedca_params_event(drv, tb); ++ break; + default: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); +--- a/src/drivers/nl80211_copy.h ++++ b/src/drivers/nl80211_copy.h +@@ -1329,6 +1329,11 @@ + * %NL80211_ATTR_MLO_TTLM_ULINK attributes are used to specify the + * TID to Link mapping for downlink/uplink traffic. + * ++ * @NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS: Updated MU-EDCA parameters from driver. ++ * This event is used to update MU-EDCA parameters in Beacon frame, which ++ * were indicated by driver and now need to be reflected in ++ * Beacon frame. ++ * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +@@ -1586,6 +1591,7 @@ enum nl80211_commands { + + NL80211_CMD_SET_TID_TO_LINK_MAPPING, + ++ NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS, + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ +@@ -2816,6 +2822,9 @@ enum nl80211_commands { + * the incoming frame RX timestamp. + * @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent + * (re)associations. ++ * @NL80211_ATTR_HE_MUEDCA_PARAMS: MU-EDCA AC parameters for the ++ * NL80211_CMD_UPDATE_HE_MUEDCA_PARAMS command. ++ * + * + * @NL80211_ATTR_PUNCT_BITMAP: (u32) Preamble puncturing bitmap, lowest + * bit corresponds to the lowest 20 MHz channel. Each bit set to 1 +@@ -3416,6 +3425,7 @@ enum nl80211_attrs { + NL80211_ATTR_WIPHY_RADIOS, + NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS, + ++ NL80211_ATTR_HE_MUEDCA_PARAMS, + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, 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..06106df76e2243 --- /dev/null +++ b/package/network/utils/iproute2/patches/400-add-nss-qdisc.patch @@ -0,0 +1,2097 @@ +--- 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,1830 @@ ++/* ++ ************************************************************************** ++ * 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSTBL_MAX + 1]; ++ struct tc_nsstbl_qopt *qopt; ++ SPRINT_BUF(b1); ++ ++ 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]); ++ ++ fprintf(f, "buffer/maxburst %s ", sprint_size(qopt->burst, b1)); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ fprintf(f, "mtu %s ", sprint_size(qopt->mtu, b1)); ++ 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const struct qdisc_util *qu, FILE *f, struct rtattr *opt) ++{ ++ struct rtattr *tb[TCA_NSSBF_MAX + 1]; ++ struct tc_nssbf_class_qopt *qopt; ++ SPRINT_BUF(b1); ++ ++ 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]); ++ ++ ++ fprintf(f, "burst %s ", sprint_size(qopt->burst, b1)); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1)); ++ fprintf(f, "mtu %s ", sprint_size(qopt->mtu, b1)); ++ ++ 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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]); ++ ++ fprintf(f, "burst %s ", sprint_size(qopt->burst, b1)); ++ tc_print_rate(PRINT_FP, NULL, "rate %s ", qopt->rate); ++ fprintf(f, "cburst %s ", sprint_size(qopt->cburst, b1)); ++ tc_print_rate(PRINT_FP, NULL, "crate %s ", qopt->crate); ++ fprintf(f, "priority %u ", qopt->priority); ++ fprintf(f, "quantum %s ", sprint_size(qopt->quantum, b1)); ++ fprintf(f, "overhead %s ", sprint_size(qopt->overhead, b1)); ++ ++ 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(const struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n, const char *dev) ++{ ++ 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(const 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..46999ee257df85 --- /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(const 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(const 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(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/paypal.png b/paypal.png new file mode 100644 index 00000000000000..efc29921bf61c5 Binary files /dev/null and b/paypal.png differ diff --git a/target/linux/qualcommax/Makefile b/target/linux/qualcommax/Makefile index 3c9bb840e31ca6..850191b2d0d878 100644 --- a/target/linux/qualcommax/Makefile +++ b/target/linux/qualcommax/Makefile @@ -16,6 +16,8 @@ DEFAULT_PACKAGES += \ kmod-leds-gpio kmod-gpio-button-hotplug \ kmod-qca-nss-dp kmod-ath11k-ahb \ wpad-basic-mbedtls uboot-envtools \ - e2fsprogs kmod-fs-ext4 losetup + e2fsprogs kmod-fs-ext4 losetup \ + kmod-qca-nss-drv kmod-qca-nss-ecm \ + kmod-qca-nss-drv-bridge-mgr $(eval $(call BuildTarget)) diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq6018-512m.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq6018-512m.dtsi index d46a852404d946..51e0e7d8120db5 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq6018-512m.dtsi +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq6018-512m.dtsi @@ -21,3 +21,7 @@ reg = <0x0 0x4ab00000 0x0 0x03700000>; no-map; }; + +&ramoops_region { + reg = <0x0 0x4e200000 0x0 0x100000>; +}; 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/ipq8070-rm2-6.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-rm2-6.dts index 5d3123f75ea3ff..52a9468f99be54 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-rm2-6.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8070-rm2-6.dts @@ -5,6 +5,7 @@ #include "ipq8074-512m.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/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 f8fe6b42f80893..bcf028522009d9 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/ipq8071-mf269.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-mf269.dts index 866ed5e9c35011..c5b21bd29abc6c 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-mf269.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8071-mf269.dts @@ -5,6 +5,7 @@ #include "ipq8074-512m.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-aw1000.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-aw1000.dts index c85e9f1993aa33..fbad0e79e5010c 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-aw1000.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-aw1000.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-ax880.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax880.dts index 23e89a9ae42d31..af5a497b39acac 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax880.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-ax880.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-mx5300.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx5300.dts index 9d28f2ad0d4aec..6e2e95c00a3de8 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx5300.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx5300.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-mx8500.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx8500.dts index 70f4438ab0c530..a4fcb3e18d0343 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx8500.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-mx8500.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-sax1v1k.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-sax1v1k.dts index fbb652a097b05f..4ce99b1836675e 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-sax1v1k.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-sax1v1k.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 f410f79495ef18..5762899889d126 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/ipq8072-zbt-z800ax.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-zbt-z800ax.dts index 814a7cb2bd9df1..2398bf92959c99 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-zbt-z800ax.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8072-zbt-z800ax.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-512m.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi index dace4008b38bdf..48f6345c948981 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-512m.dtsi @@ -17,3 +17,7 @@ &m3_dump_region { reg = <0x0 0x4e800000 0x0 0x100000>; }; + +&ramoops_region { + reg = <0x0 0x4e900000 0x0 0x100000>; +}; 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..ff07e3871335ad --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-nss.dtsi @@ -0,0 +1,286 @@ +// 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; + }; + }; + }; + + nss-macsec0 { + compatible = "qcom,nss-macsec"; + mdiobus = <&mdio>; + phy_addr = <0x18>; + phy_access_mode = <0x00>; + }; + + nss-macsec1 { + compatible = "qcom,nss-macsec"; + mdiobus = <&mdio>; + phy_addr = <0x1c>; + phy_access_mode = <0x00>; + }; + +}; 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 36e315cc8078bd..5331f985875dc7 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-rt-ax89x.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rt-ax89x.dts index 4af942c2892877..694b5aabe1b7b0 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rt-ax89x.dts +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-rt-ax89x.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-sxk80.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-sxk80.dtsi index 7f8b813749116f..7a29f945d55537 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-sxk80.dtsi +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8074-sxk80.dtsi @@ -8,6 +8,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 347bb6e8abe539..5f463b6c62e2cc 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/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi index 13ce8d16016860..741c00bdcb9f56 100644 --- a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq8174-mx4200.dtsi @@ -4,6 +4,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/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..fc126abab03371 --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_recycle.c @@ -0,0 +1,807 @@ +/* + * 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 + +static int skb_recycling_enable = 1; +inline struct sk_buff *skb_recycler_alloc(struct net_device *dev, + unsigned int length, bool reset_skb) +{ + unsigned long flags; + struct sk_buff_head *h; + struct sk_buff *skb = NULL; + struct sk_buff *ln = NULL; + + /* Allocate the recycled skbs if the skb_recycling_enable */ + if (unlikely(!skb_recycling_enable)) { + return 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. + * For buffers recycled by PPE DS rings, the packets wouldnt + * have been processed by host and hence shinfo reset can be + * avoided. Avoid it if specifically requested for it + * (by DS rings), and the buffer is found to be recycled by + * DS previously + */ + + 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; + + /* Consume the skbs if the skb_recycling_enable */ + if (unlikely(!skb_recycling_enable)) { + return false; + } + + /* 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; + if (unlikely(!skb_recycling_enable)) { + return false; + } + 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; + + /* Allocate the recycled skbs if the skb_recycling_enable */ + if (unlikely(!skb_recycling_enable)) { + return false; + } + + 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 */ + +/* procfs: skb_recycler_enable + * By default, recycler is disabled for QSDK_512 profile. + * Can be enabled for alder/miami QSDK_512 profile. + */ +static int proc_skb_recycler_enable_show(struct seq_file *seq, void *v) { + seq_printf(seq, "%d\n", skb_recycling_enable); + return 0; +} + +static int proc_skb_recycle_enable_open(struct inode *inode, struct file *file) { + return single_open(file, proc_skb_recycler_enable_show, + pde_data(inode)); +} + +static ssize_t proc_skb_recycle_enable_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) { + int ret; + int enable; + 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, &enable); + if (ret == 0 && enable >= 0) + skb_recycling_enable = enable; + + return count; +} + +static const struct proc_ops proc_skb_recycle_enable_fops = { + .proc_open = proc_skb_recycle_enable_open, + .proc_read = seq_read, + .proc_write = proc_skb_recycle_enable_write, + .proc_release = single_release, +}; + +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 + + if (!proc_create("skb_recycler_enable", + S_IRUGO | S_IWUGO, + proc_net_skbrecycler, + &proc_skb_recycle_enable_fops)) + pr_err("cannot create proc net skb_recycle enable\n"); + +} + +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 CONFIG_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..bfc46920177fc1 --- /dev/null +++ b/target/linux/qualcommax/files/net/core/skbuff_recycle.h @@ -0,0 +1,186 @@ +/* + * 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 + +#ifndef CONFIG_SKB_RECYCLE_SIZE +#define CONFIG_SKB_RECYCLE_SIZE 2304 +#endif +#define SKB_RECYCLE_SIZE CONFIG_SKB_RECYCLE_SIZE +#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; + + if (skb->active_extensions) + 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 reset_skb); +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); +void skb_recycler_clear_flags(struct sk_buff *skb); +#else +#define skb_recycler_init() {} +#define skb_recycler_alloc(dev, len, reset_skb) NULL +#define skb_recycler_consume(skb) false +#define skb_recycler_consume_list_fast(skb_list) false +#define skb_recycler_print_all_lists() false +#define skb_recycler_clear_flags(skb) {} +#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/image/ipq807x.mk b/target/linux/qualcommax/image/ipq807x.mk index faefb479c24ea7..d640704ac29285 100644 --- a/target/linux/qualcommax/image/ipq807x.mk +++ b/target/linux/qualcommax/image/ipq807x.mk @@ -187,7 +187,7 @@ define Device/linksys_mx5300 $(call Device/linksys_mx) DEVICE_MODEL := MX5300 DEVICE_PACKAGES += kmod-rtc-ds1307 ipq-wifi-linksys_mx5300 \ - kmod-ath10k-ct ath10k-firmware-qca9984-ct + kmod-ath10k ath10k-firmware-qca9984 endef TARGET_DEVICES += linksys_mx5300 @@ -368,7 +368,7 @@ define Device/xiaomi_ax3600 DEVICE_DTS_CONFIG := config@ac04 SOC := ipq8071 KERNEL_SIZE := 36608k - DEVICE_PACKAGES := ipq-wifi-xiaomi_ax3600 kmod-ath10k-ct-smallbuffers ath10k-firmware-qca9887-ct + DEVICE_PACKAGES := ipq-wifi-xiaomi_ax3600 kmod-ath10k-smallbuffers ath10k-firmware-qca9887 ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) ARTIFACTS := initramfs-factory.ubi ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel @@ -387,7 +387,7 @@ define Device/xiaomi_ax9000 SOC := ipq8072 KERNEL_SIZE := 57344k DEVICE_PACKAGES := ipq-wifi-xiaomi_ax9000 kmod-ath11k-pci ath11k-firmware-qcn9074 \ - kmod-ath10k-ct ath10k-firmware-qca9887-ct + kmod-ath10k ath10k-firmware-qca9887 ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) ARTIFACTS := initramfs-factory.ubi ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-uImage.itb | ubinize-kernel diff --git a/target/linux/qualcommax/ipq807x/target.mk b/target/linux/qualcommax/ipq807x/target.mk index 22a9b78d592df4..b4af8fa4233489 100644 --- a/target/linux/qualcommax/ipq807x/target.mk +++ b/target/linux/qualcommax/ipq807x/target.mk @@ -1,6 +1,6 @@ SUBTARGET:=ipq807x BOARDNAME:=Qualcomm Atheros IPQ807x -DEFAULT_PACKAGES += kmod-phy-aquantia ath11k-firmware-ipq8074 +DEFAULT_PACKAGES += kmod-phy-aquantia ath11k-firmware-ipq8074 kmod-qca-nss-ecm kmod-qca-nss-drv define Target/Description Build firmware images for Qualcomm Atheros IPQ807x based boards. 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..7f95b0110c9ec5 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 @@ -23,7 +23,7 @@ Signed-off-by: Robert Marko #size-cells = <2>; ranges; -+ nss@40000000 { ++ nss_region: nss@40000000 { + no-map; + reg = <0x0 0x40000000 0x0 0x01000000>; + }; 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..056fba00aa5aa3 --- /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, +@@ -2031,6 +2053,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 = { +@@ -4226,13 +4320,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, +@@ -4324,6 +4412,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, +@@ -4332,6 +4528,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[] = { +@@ -4563,6 +4760,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..96577d0eda09a9 --- /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 +@@ -4413,10 +4413,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", +@@ -4431,10 +4431,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..6e4adf685b2425 --- /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 +@@ -2127,9 +2127,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..46228b4b899fcf --- /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 +@@ -2933,6 +2933,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 +@@ -215,6 +215,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 +@@ -2268,4 +2268,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: +@@ -1776,6 +1782,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; +@@ -2408,6 +2417,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 +@@ -668,6 +668,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 +@@ -198,6 +198,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) +@@ -3868,6 +3871,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; +@@ -3889,6 +3896,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; +@@ -6336,6 +6346,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 +@@ -1699,6 +1699,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 +@@ -630,6 +630,7 @@ enum nf_ct_sysctl_index { + #endif + NF_SYSCTL_CT_PROTO_TCP_LOOSE, + NF_SYSCTL_CT_PROTO_TCP_LIBERAL, ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, + NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, + NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, +@@ -834,6 +835,14 @@ static struct ctl_table nf_ct_sysctl_tab + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .maxlen = sizeof(u8), ++ .mode = 0644, ++ .proc_handler = proc_dou8vec_minmax, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE, ++ }, + [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { + .procname = "nf_conntrack_tcp_ignore_invalid_rst", + .maxlen = sizeof(u8), +@@ -1035,6 +1044,7 @@ static void nf_conntrack_standalone_init + + XASSIGN(LOOSE, &tn->tcp_loose); + XASSIGN(LIBERAL, &tn->tcp_be_liberal); ++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); + XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); + XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); + #undef XASSIGN diff --git a/target/linux/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..f7271a6886a98b --- /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 +@@ -255,6 +256,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); +@@ -3468,7 +3488,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); +@@ -3500,13 +3523,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; + } + +@@ -3524,6 +3574,13 @@ ppp_disconnect_channel(struct channel *p + pch->ppp = NULL; + write_unlock_bh(&pch->upl); + if (ppp) { ++ if (ppp->dev) { ++ dev_hold(ppp->dev); ++ raw_notifier_call_chain(&ppp_channel_connection_notifier_list, ++ PPP_CHANNEL_DISCONNECT, ppp->dev); ++ dev_put(ppp->dev); ++ } ++ + /* remove it from the ppp unit's list */ + ppp_lock(ppp); + list_del(&pch->clist); +@@ -3603,6 +3660,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); +@@ -3619,6 +3892,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 +@@ -1761,6 +1761,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 +@@ -2126,6 +2156,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..e615943782f60b --- /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[] = { + { +@@ -5911,6 +5912,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); + } + +@@ -6464,6 +6470,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 { + struct mutex 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..0f0bf62fb819cb --- /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. + * +@@ -1228,6 +1243,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 { +@@ -1872,6 +1904,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; + +@@ -2317,6 +2350,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; +@@ -2382,6 +2424,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; + } + +@@ -2403,6 +2454,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; + +@@ -2425,6 +2477,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 */ +@@ -2747,6 +2808,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(); + +@@ -2786,6 +2849,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"); +@@ -2834,6 +2903,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); + } + +@@ -4086,8 +4165,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) +@@ -5216,15 +5506,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. */ +@@ -5474,8 +5767,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..baef78525a3e13 --- /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 +@@ -3871,10 +3871,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; +@@ -3896,9 +3892,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; +@@ -4507,6 +4500,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; + } + +@@ -5525,11 +5522,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, +@@ -5546,9 +5549,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..4a3782466c864c --- /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 +@@ -4708,6 +4708,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 +@@ -5252,6 +5261,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) + { +@@ -2395,4 +2396,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 +@@ -1083,6 +1083,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; +@@ -752,6 +753,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 +@@ -762,6 +762,7 @@ typedef unsigned char *sk_buff_data_t; + * @offload_fwd_mark: Packet was L2-forwarded in hardware + * @offload_l3_fwd_mark: Packet was L3-forwarded in hardware + * @tc_skip_classify: do not classify packet. set by IFB device ++ * @tc_skip_classify_offload: do not classify packet set by offload IFB device + * @tc_at_ingress: used within tc_classify to distinguish in/egress + * @redirected: packet was redirected by packet classifier + * @from_ingress: packet was redirected from the ingress path +@@ -940,6 +941,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..95ae02b7ee58b2 --- /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 +@@ -403,6 +403,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..2c431a5d257731 --- /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 +@@ -2988,6 +2988,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. +@@ -3696,6 +3710,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. + */ +@@ -3887,6 +3923,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..86c9688adeaf7c --- /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 +@@ -592,4 +592,9 @@ static inline void ip_tunnel_info_opts_s + + #endif /* CONFIG_INET */ + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr); ++void ip6_update_offload_stats(struct net_device *dev, void *ptr); ++/* QCA NSS Clients Support - End */ ++ + #endif /* __NET_IP_TUNNELS_H */ +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -2414,6 +2414,26 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Client Support - Start */ ++/* ++ * Update offload stats ++ */ ++void ip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ip6_update_offload_stats); ++/* QCA NSS Client Support - End */ ++ + struct net *ip6_tnl_get_link_net(const struct net_device *dev) + { + struct ip6_tnl *tunnel = netdev_priv(dev); +--- a/net/ipv6/sit.c ++++ b/net/ipv6/sit.c +@@ -1734,6 +1734,23 @@ nla_put_failure: + return -EMSGSIZE; + } + ++/* QCA NSS Clients Support - Start */ ++void ipip6_update_offload_stats(struct net_device *dev, void *ptr) ++{ ++ struct pcpu_sw_netstats *tstats = per_cpu_ptr(dev->tstats, 0); ++ const struct pcpu_sw_netstats *offload_stats = ++ (struct pcpu_sw_netstats *)ptr; ++ ++ u64_stats_update_begin(&tstats->syncp); ++ u64_stats_add(&tstats->tx_packets, u64_stats_read(&offload_stats->tx_packets)); ++ u64_stats_add(&tstats->tx_bytes, u64_stats_read(&offload_stats->tx_bytes)); ++ u64_stats_add(&tstats->rx_packets, u64_stats_read(&offload_stats->rx_packets)); ++ u64_stats_add(&tstats->rx_bytes, u64_stats_read(&offload_stats->rx_bytes)); ++ u64_stats_update_end(&tstats->syncp); ++} ++EXPORT_SYMBOL(ipip6_update_offload_stats); ++/* QCA NSS Clients Support - End */ ++ + static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, + [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, diff --git a/target/linux/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..b80d57bdff4519 --- /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, +@@ -2600,6 +2631,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), +@@ -2671,6 +2705,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..1cc92442e9370a --- /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 +@@ -3758,6 +3758,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 +@@ -3896,6 +3922,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..9c80e643474a65 --- /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 +@@ -2420,7 +2420,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 +@@ -1737,7 +1737,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..8c0c601423a2b7 --- /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); + +@@ -2270,4 +2271,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 +@@ -657,6 +657,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 +@@ -89,6 +89,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..23d5089631e91f --- /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 +@@ -415,6 +415,8 @@ static int crypto_authenc_create(struct + enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME) + goto err_free_inst; + ++ inst->alg.base.cra_flags |= (auth_base->cra_flags | ++ enc->base.cra_flags) & CRYPTO_ALG_NOSUPP_SG; + inst->alg.base.cra_priority = enc->base.cra_priority * 10 + + auth_base->cra_priority; + inst->alg.base.cra_blocksize = enc->base.cra_blocksize; +--- a/include/linux/crypto.h ++++ b/include/linux/crypto.h +@@ -86,6 +86,11 @@ + #define CRYPTO_NOLOAD 0x00008000 + + /* ++ * Set this flag if algorithm does not support SG list transforms ++ */ ++#define CRYPTO_ALG_NOSUPP_SG 0x0000c000 ++ ++/* + * The algorithm may allocate memory during request processing, i.e. during + * encryption, decryption, or hashing. Users can request an algorithm with this + * flag unset if they can't handle memory allocation failures. +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -3,6 +3,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -657,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; + +@@ -668,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); +@@ -889,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; +@@ -896,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 +@@ -695,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; + +@@ -706,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); +@@ -933,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; +@@ -944,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/0606-1-qca-nss-ecm-bridge-Fixes-for-Bridge-VLAN-Filtering.patch b/target/linux/qualcommax/patches-6.6/0606-1-qca-nss-ecm-bridge-Fixes-for-Bridge-VLAN-Filtering.patch new file mode 100644 index 00000000000000..b4d5f89dd559a6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0606-1-qca-nss-ecm-bridge-Fixes-for-Bridge-VLAN-Filtering.patch @@ -0,0 +1,341 @@ +From 7732ede3f72eebb8742e17e61e07e9286c442aec Mon Sep 17 00:00:00 2001 +From: Vishnu Vardhan Bantanahal +Date: Mon, 15 May 2023 17:56:04 +0530 +Subject: [PATCH 277/281] bridge: Fixes for Bridge VLAN Filtering + +1. Fix function to check for bridge master status while checking +for Bridge VLAN filter feature is enabled on bridge slave ports. +2. Disable default PVID for bridges during device registration in +the system. +Change-Id: Ibea6559c1b0700a2300b60e20d57b7818e23a8a8 +Signed-off-by: Vishnu Vardhan Bantanahal + +bridge: Fix Bridge VLAN stats update +This patch fixes Bridge VLAN stats update for both bridge master +and bridge slave. +Change-Id: Ia26f4c71e83e27dd83336815cda5c05c8c3f24ff +Signed-off-by: Vishnu Vardhan Bantanahal + +bridge: Add bridge VLAN filter APIs for offload for 6.1 Kernel + +Change-Id: I54e44c26664f86ae024f54605a032713a9a3eee5 +Signed-off-by: Vishnu Vardhan Bantanahal +--- + include/linux/if_bridge.h | 29 +++++- + include/linux/netdevice.h | 2 +- + net/bridge/br.c | 4 + + net/bridge/br_if.c | 11 ++- + net/bridge/br_private.h | 1 + + net/bridge/br_vlan.c | 186 +++++++++++++++++++++++++++++++++++++- + net/core/dev.c | 2 +- + 7 files changed, 227 insertions(+), 8 deletions(-) + +--- a/include/linux/if_bridge.h ++++ b/include/linux/if_bridge.h +@@ -128,6 +128,12 @@ int br_vlan_get_info_rcu(const struct ne + bool br_mst_enabled(const struct net_device *dev); + int br_mst_get_info(const struct net_device *dev, u16 msti, unsigned long *vids); + int br_mst_get_state(const struct net_device *dev, u16 msti, u8 *state); ++ ++extern struct net_device *br_fdb_find_vid_by_mac(struct net_device *dev, u8 *mac, u16 *vid); ++extern int br_vlan_get_tag_skb(const struct sk_buff *skb, u16 *vid); ++extern int br_dev_is_vlan_filter_enabled(struct net_device *dev); ++extern int br_vlan_update_stats(struct net_device* dev, u32 vid, u64 rx_bytes, u64 rx_packets, u64 tx_bytes, u64 tx_packets); ++extern int br_vlan_get_info_rcu(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo); + #else + static inline bool br_vlan_enabled(const struct net_device *dev) + { +@@ -149,8 +155,27 @@ static inline int br_vlan_get_pvid_rcu(c + return -EINVAL; + } + +-static inline int br_vlan_get_info(const struct net_device *dev, u16 vid, +- struct bridge_vlan_info *p_vinfo) ++static inline int br_vlan_get_info(const struct net_device *dev, u16 vid, struct bridge_vlan_info *p_vinfo) ++{ ++ return -EINVAL; ++} ++ ++static inline struct net_device *br_fdb_find_vid_by_mac(struct net_device *dev, u8 *mac, u16 *vid) ++{ ++ return NULL; ++} ++ ++static inline int br_vlan_get_tag_skb(const struct sk_buff *skb, u16 *vid) ++{ ++ return -EINVAL; ++} ++ ++static inline int br_dev_is_vlan_filter_enabled(const struct net_device *dev) ++{ ++ return -EINVAL; ++} ++ ++static inline int br_vlan_update_stats(struct net_device* dev, u32 vid, u64 rx_bytes, u64 rx_packets, u64 tx_bytes, u64 tx_packets) + { + return -EINVAL; + } +--- a/net/bridge/br.c ++++ b/net/bridge/br.c +@@ -42,6 +42,10 @@ static int br_device_event(struct notifi + return notifier_from_errno(err); + + if (event == NETDEV_REGISTER) { ++#if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) ++ br_vlan_disable_default_pvid(netdev_priv(dev)); ++#endif ++ + /* register of bridge completed, add sysfs entries */ + err = br_sysfs_addbr(dev); + if (err) +--- a/net/bridge/br_if.c ++++ b/net/bridge/br_if.c +@@ -800,9 +800,12 @@ struct net_device *br_port_dev_get(struc + struct sk_buff *skb, + unsigned int cookie) + { ++#if !IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) + struct net_bridge_fdb_entry *fdbe; + struct net_bridge *br; ++#endif + struct net_device *netdev = NULL; ++ u16 __maybe_unused vid; + + /* Is this a bridge? */ + if (!(dev->priv_flags & IFF_EBRIDGE)) +@@ -831,14 +834,20 @@ struct net_device *br_port_dev_get(struc + * determine the port to use - fall back to using FDB + */ + ++#if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) ++ /* Lookup the fdb entry and get reference to the port dev. ++ * dev_hold() is done as part of br_fdb_find_vid_by_mac() ++ */ ++ netdev = br_fdb_find_vid_by_mac(dev, addr, &vid); ++#else + 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); + } ++#endif ++ + out: + rcu_read_unlock(); + return netdev; +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -1563,6 +1563,7 @@ void br_vlan_fill_forward_path_pvid(stru + int br_vlan_fill_forward_path_mode(struct net_bridge *br, + struct net_bridge_port *dst, + struct net_device_path *path); ++void br_vlan_disable_default_pvid(struct net_bridge *br); + + static inline struct net_bridge_vlan_group *br_vlan_group( + const struct net_bridge *br) +--- a/net/bridge/br_vlan.c ++++ b/net/bridge/br_vlan.c +@@ -933,8 +933,190 @@ int br_vlan_get_proto(const struct net_d + } + EXPORT_SYMBOL_GPL(br_vlan_get_proto); + ++/* ++ * br_vlan_get_tag_skb() ++ * Returns VLAN tag is its found valid in skb. ++ */ ++int br_vlan_get_tag_skb(const struct sk_buff *skb, u16 *vid) ++{ ++ return br_vlan_get_tag(skb, vid); ++ ++} ++EXPORT_SYMBOL_GPL(br_vlan_get_tag_skb); ++ ++/* ++ * br_dev_is_vlan_filter_enabled() ++ * Caller should ensure to hold rcu_lock() ++ * Returns 0, when device(port or bridge device) has a valid bridge ++ * vlan filter configuration and returns error otherwise. ++ */ ++int br_dev_is_vlan_filter_enabled(struct net_device *dev) ++{ ++ struct net_bridge_port *p; ++ struct net_bridge_vlan_group *vg = NULL; ++ struct net_device *master = NULL; ++ ++ if (!dev) { ++ return -ENODEV; ++ } ++ ++ if (netif_is_bridge_master(dev)) { ++ /* ++ * Its a bridge device ++ */ ++ if (!br_vlan_enabled(dev)) { ++ return -ENOENT; ++ } ++ ++ vg = br_vlan_group(netdev_priv(dev)); ++ } else if (dev->priv_flags & IFF_BRIDGE_PORT) { ++ /* ++ * It's a bridge port ++ */ ++ master = netdev_master_upper_dev_get_rcu(dev); ++ if (!master) { ++ return -EINVAL; ++ } ++ ++ if (!br_vlan_enabled(master)) { ++ return -ENOENT; ++ } ++ ++ p = br_port_get_rcu(dev); ++ if (p) ++ vg = nbp_vlan_group(p); ++ } else { ++ /* ++ * Neither a bridge device or port ++ */ ++ return -EINVAL; ++ } ++ ++ if (vg != NULL && vg->num_vlans) { ++ return 0; ++ } ++ ++ return -ENXIO; ++} ++EXPORT_SYMBOL_GPL(br_dev_is_vlan_filter_enabled); ++ ++/* ++ * br_fdb_find_vid_by_mac() ++ * Caller ensures to ensure rcu_lock() is taken. ++ * Returns 0 in case of lookup was performed. ++ * Look up the bridge fdb table for the mac-address & find associated ++ * VLAN id associated with it. ++ * vid is non-zero for succesfull lookup, otherwise 0. ++ * We dev_hold() on the returned device, caller will release this hold. ++ */ ++struct net_device *br_fdb_find_vid_by_mac(struct net_device *dev, u8 *mac, u16 *vid) ++{ ++ struct net_bridge *br; ++ struct net_bridge_fdb_entry *f; ++ struct net_device *netdev = NULL; ++ ++ if (!mac) { ++ return NULL; ++ } ++ ++ if (!dev || !netif_is_bridge_master(dev)) { ++ return NULL; ++ } ++ ++ br = netdev_priv(dev); ++ if (!br) { ++ return NULL; ++ } ++ ++ hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { ++ if (ether_addr_equal(f->key.addr.addr, mac)) { ++ *vid = f->key.vlan_id; ++ if (f->dst) { ++ netdev = f->dst->dev; ++ dev_hold(netdev); ++ break; ++ } ++ } ++ } ++ return netdev; ++} ++EXPORT_SYMBOL_GPL(br_fdb_find_vid_by_mac); ++ ++/* ++ * br_vlan_update_stats() ++ * Update bridge VLAN filter statistics. ++ */ ++int br_vlan_update_stats(struct net_device *dev, u32 vid, u64 rx_bytes, u64 rx_packets, u64 tx_bytes, u64 tx_packets) ++{ ++ struct net_bridge_port *p; ++ struct net_bridge_vlan *v; ++ struct pcpu_sw_netstats *stats; ++ const struct net_bridge *br; ++ struct net_bridge_vlan_group *vg; ++ struct net_device *brdev; ++ ++ if (!dev) { ++ return -ENODEV; ++ } ++ ++ if (!netif_is_bridge_port(dev) && !netif_is_bridge_master(dev)) { ++ return -EINVAL; ++ } ++ ++ rcu_read_lock(); ++ ++ brdev = dev; ++ if (!netif_is_bridge_master(dev)) { ++ brdev = netdev_master_upper_dev_get_rcu(dev); ++ if (!brdev) { ++ rcu_read_unlock(); ++ return -EPERM; ++ } ++ } ++ ++ br = netdev_priv(brdev); ++ if (!br || !br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) { ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ p = br_port_get_rcu(dev); ++ if (p) { ++ vg = nbp_vlan_group_rcu(p); ++ } else if (netif_is_bridge_master(dev)) { ++ vg = br_vlan_group(netdev_priv(dev)); ++ } else { ++ rcu_read_unlock(); ++ return -EINVAL; ++ } ++ ++ ++ if (!vg) { ++ rcu_read_unlock(); ++ return -ENXIO; ++ } ++ ++ v = br_vlan_find(vg, vid); ++ if (!v || !br_vlan_should_use(v)) { ++ rcu_read_unlock(); ++ return -ENOENT; ++ } ++ ++ stats = this_cpu_ptr(v->stats); ++ u64_stats_update_begin(&stats->syncp); ++ u64_stats_add(&stats->rx_bytes, rx_bytes); ++ u64_stats_add(&stats->rx_packets, rx_packets); ++ u64_stats_add(&stats->tx_bytes, tx_bytes); ++ u64_stats_add(&stats->tx_packets, tx_packets); ++ u64_stats_update_end(&stats->syncp); ++ ++ rcu_read_unlock(); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(br_vlan_update_stats); ++ + int __br_vlan_set_proto(struct net_bridge *br, __be16 proto, +- struct netlink_ext_ack *extack) ++ struct netlink_ext_ack *extack) + { + struct switchdev_attr attr = { + .orig_dev = br->dev, +@@ -1068,7 +1250,7 @@ static bool vlan_default_pvid(struct net + return false; + } + +-static void br_vlan_disable_default_pvid(struct net_bridge *br) ++void br_vlan_disable_default_pvid(struct net_bridge *br) + { + struct net_bridge_port *p; + u16 pvid = br->default_pvid; diff --git a/target/linux/qualcommax/patches-6.6/0911-arm64-dts-qcom-ipq8074-add-ramoops.patch b/target/linux/qualcommax/patches-6.6/0911-arm64-dts-qcom-ipq8074-add-ramoops.patch new file mode 100644 index 00000000000000..2aad7fc3f89b40 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0911-arm64-dts-qcom-ipq8074-add-ramoops.patch @@ -0,0 +1,17 @@ +--- a/arch/arm64/boot/dts/qcom/ipq8074.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq8074.dtsi +@@ -198,6 +198,14 @@ + no-map; + reg = <0x0 0x51000000 0x0 0x100000>; + }; ++ ++ ramoops_region: ramoops@51200000 { ++ compatible = "ramoops"; ++ reg = <0x0 0x51200000 0x0 0x100000>; ++ no-map; ++ record-size = <0x4000>; ++ console-size = <0x4000>; ++ }; + }; + + firmware { diff --git a/target/linux/qualcommax/patches-6.6/0912-arm64-dts-qcom-ipq6018-add-ramoops.patch b/target/linux/qualcommax/patches-6.6/0912-arm64-dts-qcom-ipq6018-add-ramoops.patch new file mode 100644 index 00000000000000..4873f3cac754f4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0912-arm64-dts-qcom-ipq6018-add-ramoops.patch @@ -0,0 +1,16 @@ +--- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi +@@ -223,6 +223,13 @@ + reg = <0x0 0x4ab00000 0x0 0x5500000>; + no-map; + }; ++ ++ ramoops_region: ramoops@50000000 { ++ compatible = "ramoops"; ++ reg = <0x0 0x50000000 0x0 0x100000>; ++ no-map; ++ record-size = <0x1000>; ++ }; + }; + + smem { diff --git a/target/linux/qualcommax/patches-6.6/0980-2-Revert-crypto-api-disallow-identical-driver-names.patch b/target/linux/qualcommax/patches-6.6/0980-2-Revert-crypto-api-disallow-identical-driver-names.patch new file mode 100644 index 00000000000000..3f7f58dfee5af2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0980-2-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/0980-3-mtd-silence-UBI-NAND-warnings.patch b/target/linux/qualcommax/patches-6.6/0980-3-mtd-silence-UBI-NAND-warnings.patch new file mode 100644 index 00000000000000..646e683dc8df70 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0980-3-mtd-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); diff --git a/target/linux/qualcommax/patches-6.6/0981-1-qca-skb_recycler-support.patch b/target/linux/qualcommax/patches-6.6/0981-1-qca-skb_recycler-support.patch new file mode 100644 index 00000000000000..a686c323c31e17 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0981-1-qca-skb_recycler-support.patch @@ -0,0 +1,431 @@ +--- 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 +@@ -1059,6 +1059,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 */ +@@ -1244,7 +1248,7 @@ static inline void kfree_skb_list(struct + 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) +@@ -1256,6 +1260,9 @@ static inline void consume_skb(struct sk + 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,52 @@ 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 SKB_RECYCLER_PREALLOC ++ bool "Enable preallocation of SKBs" ++ depends on SKB_RECYCLER ++ default n ++ help ++ Preallocates SKBs in recycling lists and the number of ++ SKBs are configured through CONFIG_SKB_RECYCLE_MAX_PREALLOC_SKBS. ++ This needs SKB_RECYCLER to be enabled. ++ The number of preallocated SKBs can be passed using ++ SKB_RECYCLE_MAX_PREALLOC_SKBS. ++ ++config SKB_RECYCLE_MAX_PREALLOC_SKBS ++ int "Number of SKBs to be preallocated" ++ depends on SKB_RECYCLER_PREALLOC ++ default 16384 ++ help ++ Number of SKBs each of 4K size to be preallocated for recycling ++ ++config SKB_RECYCLE_SIZE ++ int "Minimum size for a recycled buffer" ++ depends on SKB_RECYCLER ++ default 2304 ++ help ++ Minimum size for a recycled buffer ++ ++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 +@@ -6051,10 +6051,16 @@ static int process_backlog(struct napi_s + + 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,38 @@ + + #include "dev.h" + #include "sock_destructor.h" ++#include "skbuff_recycle.h" ++ ++struct kmem_cache *skb_data_cache; ++struct kmem_cache *skb_data_cache_2100; ++/* ++ * 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) ++/* ++ * Both caches are kept same size when recycler is enabled so that all the ++ * skbs could be recycled. 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))) ++#define SKB_DATA_CACHE_SIZE_2100 SKB_DATA_CACHE_SIZE ++#else ++/* ++ * DATA CACHE is 2368 for 64bit arch, 2176 for 32bit arch ++ * DATA_CACHE_2100 is 2496 for 64bit arch, 2432 for 32bit arch ++ * DATA CACHE size should always be lesser than that of DATA_CACHE_2100 size ++ */ ++#if defined(__LP64__) ++#define SKB_DATA_CACHE_SIZE (SKB_DATA_ALIGN(1984 + NET_SKB_PAD) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#define SKB_DATA_CACHE_SIZE_2100 (SKB_DATA_ALIGN(2100 + 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))) ++#define SKB_DATA_CACHE_SIZE_2100 (SKB_DATA_ALIGN(2100 + 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 +583,31 @@ static void *kmalloc_reserve(unsigned in + 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_data_cache, flags, node); ++ goto out; ++ } else if (obj_size > SZ_2K && obj_size <= SKB_DATA_CACHE_SIZE_2100) { ++ obj = kmem_cache_alloc_node(skb_data_cache_2100, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, ++ node); ++ *size = SKB_DATA_CACHE_SIZE_2100; + 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_2100, flags, node); + goto out; + } + +@@ -648,10 +690,12 @@ struct sk_buff *__alloc_skb(unsigned int + * 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 +730,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 +740,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, true); ++ 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 +815,7 @@ struct sk_buff *__netdev_alloc_skb(struc + if (pfmemalloc) + skb->pfmemalloc = 1; + skb->head_frag = 1; ++#endif + + skb_success: + skb_reserve(skb, NET_SKB_PAD); +@@ -817,7 +886,8 @@ struct sk_buff *__napi_alloc_skb(struct + 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 +1045,7 @@ static void skb_free_head(struct sk_buff + } + } + +-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 +1088,7 @@ exit: + /* + * 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 +1352,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 +1360,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 +@@ -2112,6 +2216,8 @@ int pskb_expand_head(struct sk_buff *skb + + 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) +@@ -4865,6 +4971,14 @@ 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); ++ skb_data_cache_2100 = kmem_cache_create_usercopy("skb_data_cache_2100", ++ SKB_DATA_CACHE_SIZE_2100, ++ 0, SLAB_PANIC, 0, SKB_DATA_CACHE_SIZE, ++ NULL); + skbuff_cache = kmem_cache_create_usercopy("skbuff_head_cache", + sizeof(struct sk_buff), + 0, +@@ -4890,6 +5004,7 @@ void __init skb_init(void) + SKB_SMALL_HEAD_HEADROOM, + NULL); + skb_extensions_init(); ++ skb_recycler_init(); + } + + static int +@@ -6393,6 +6508,8 @@ static int pskb_carve_inside_header(stru + 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; +@@ -6509,6 +6626,8 @@ static int pskb_carve_inside_nonlinear(s + 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;