penguin/monok8s

k8s image for Mono Gateway Dev Kit

commit 9c6fb5194ea9ab384301252d9b3f4d3d0ca4b9c1eb28727d9fd63ed95a483e1c

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-05-08T21:23:55Z
subjectport ASK kernel patchset to 6.18 application state
commit 9c6fb5194ea9ab384301252d9b3f4d3d0ca4b9c1eb28727d9fd63ed95a483e1c
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-05-08T21:23:55Z

    port ASK kernel patchset to 6.18 application state
---
 docker/ask.Dockerfile                              |  244 +++--
 kernel-extra.config                                |    2 +-
 .../0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch  |   76 ++
 ...ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch |   71 ++
 ...ernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch | 1113 ++++++++++++++++++++
 .../0062-drivers__net__ppp__ppp_generic.c.patch    |   30 +
 ...-drivers__staging__fsl_qbman__qman_high.c.patch |  371 +++++++
 .../kernel/0071-include__linux__if_bridge.h.patch  |   28 +
 patches/ask/kernel/0078-include__net__xfrm.h.patch |   76 ++
 ...__linux__netfilter__nfnetlink_conntrack.h.patch |   36 +
 .../kernel/0100-net__bridge__br_private.h.patch    |   28 +
 patches/ask/kernel/0103-net__core__dev.c.patch     |  131 +++
 .../ask/kernel/0108-net__ipv4__ip_output.c.patch   |   79 ++
 .../ask/kernel/0112-net__ipv6__ip6_output.c.patch  |   73 ++
 .../ask/kernel/0115-net__ipv6__output_core.c.patch |   38 +
 patches/ask/kernel/0119-net__key__af_key.c.patch   |  949 +++++++++++++++++
 ...24-net__netfilter__nf_conntrack_netlink.c.patch |  288 +++++
 .../ask/kernel/0129-net__wireless__Kconfig.patch   |   21 +
 patches/ask/kernel/0131-net__xfrm__Makefile.patch  |    9 +
 .../ask/kernel/0136-net__xfrm__xfrm_policy.c.patch |  163 +++
 .../ask/kernel/0137-net__xfrm__xfrm_state.c.patch  |  291 +++++
 patches/split-kernel-patch.sh                      |  249 +++++
 22 files changed, 4272 insertions(+), 94 deletions(-)

diff --git a/docker/ask.Dockerfile b/docker/ask.Dockerfile
index 16c79d8..4b2dced 100644
--- a/docker/ask.Dockerfile
+++ b/docker/ask.Dockerfile
@@ -6,6 +6,10 @@ FROM --platform=$BUILDPLATFORM ${DOCKER_IMAGE_ROOT}/build-base:${BUILD_BASE_TAG}
 # Install glibc cross-compiler for kernel and standard build dependencies
 RUN apt-get update && apt-get install -y pkg-config
 
+RUN git config --global user.email "monok8s@localhost" && \
+	git config --global user.name "monok8s authors" && \
+	git config --global --add safe.directory '*'
+
 WORKDIR /src
 
 ARG AARCH64_MUSL_CC_TAR
@@ -56,7 +60,19 @@ RUN mkdir -p ASK/sources/tarballs && \
     mv libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2 ASK/sources/tarballs/ && \
     mv libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz ASK/sources/tarballs/
 
-RUN mkdir linux && tar zxf "kernel.tar.gz" -C "linux" --strip-components=1
+RUN mkdir linux && tar zxf "kernel.tar.gz" -C "linux" --strip-components=1 && \
+    mkdir -p /src/ASK/patches/kernel/updated-patch
+
+# ASK's Kernel patches
+COPY patches/split-kernel-patch.sh /src/split-kernel-patch.sh
+COPY patches/ask/kernel/*.patch /src/ASK/patches/kernel/updated-patch/
+
+RUN chmod +x /src/split-kernel-patch.sh && \
+    PATCH_FILE="/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch" \
+    LINUX_DIR="/src/linux" \
+    OUT_DIR="/src/ASK/patches/kernel/split-002" \
+    /src/split-kernel-patch.sh split && \
+    /src/split-kernel-patch.sh apply
 
 # tclap
 RUN mkdir -p tclap && tar zxf "tclap.tar.gz" -C "tclap" --strip-components=1 && \
@@ -99,95 +115,137 @@ RUN mkdir -p libpcap && tar xf "libpcap.tar.xz" -C "libpcap" --strip-components=
     make -j$(nproc) && make install && \
     cd .. && rm -rf libpcap
 
-RUN git config --global user.email "monok8s@localhost" && \
-	git config --global user.name "monok8s authors" && \
-	git config --global --add safe.directory '*'
-
-#     cd .. && \
-#     mkdir -p fmc && \
-#     tar zxf "fmc.tar.gz" -C "fmc" --strip-components=1 && \
-#     # Handle fmc: Initialize dummy repo, patch, and build \
-# 	cd fmc && git init -q && git add -A && git commit -q -m "base" && \
-# 	git apply /src/ASK/patches/fmc/01-mono-ask-extensions.patch \
-# 	make CC="aarch64-linux-musl-gcc -static" CXX="aarch64-linux-musl-g++ -static" AR=aarch64-linux-musl-ar \
-# 		MACHINE=ls1046 \
-# 		FMD_USPACE_HEADER_PATH=../fmlib/include/fmd \
-# 		FMD_USPACE_LIB_PATH=../fmlib \
-# 		LIBXML2_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include/libxml2 \
-# 		TCLAP_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include
-
-# # -- libfci ----------------------------------------------------------------
-# 
-# $(S)/libfci:
-# 	@echo "==> libfci: build"
-# 	$(MAKE) -C $(LIBFCI_DIR) CC=$(CC) AR=$(AR)
-# 	@touch $@
-# 
-# # -- libnfnetlink (patched) ------------------------------------------------
-# 
-# $(S)/libnfnetlink:
-# 	@echo "==> libnfnetlink: fetch + patch + build"
-# 	mkdir -p $(SRCDIR)/tarballs
-# 	[ -f $(SRCDIR)/tarballs/libnfnetlink-$(LIBNFNETLINK_VER).tar.bz2 ] || \
-# 		wget -q -P $(SRCDIR)/tarballs $(LIBNFNETLINK_URL)
-# 	rm -rf $(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER)
-# 	tar xf $(SRCDIR)/tarballs/libnfnetlink-$(LIBNFNETLINK_VER).tar.bz2 -C $(SRCDIR)
-# 	cd $(SRCDIR)/libnfnetlink-$(LIBNFNETLINK_VER) && \
-# 		git init -q && git add -A && git commit -q -m "upstream" && \
-# 		git apply $(PATCHES)/libnfnetlink/01-nxp-ask-nonblocking-heap-buffer.patch && \
-# 		./configure --host=$(HOST) --prefix=$(SYSROOT) --enable-static --disable-shared -q && \
-# 		$(MAKE) -j$$(nproc) -s && $(MAKE) install -s
-# 	@touch $@
-# 
-# # -- libnetfilter_conntrack (patched) --------------------------------------
-# 
-# $(S)/libnfct: $(S)/libnfnetlink
-# 	@echo "==> libnetfilter_conntrack: fetch + patch + build"
-# 	mkdir -p $(SRCDIR)/tarballs
-# 	[ -f $(SRCDIR)/tarballs/libnetfilter_conntrack-$(LIBNFCT_VER).tar.xz ] || \
-# 		wget -q -P $(SRCDIR)/tarballs $(LIBNFCT_URL)
-# 	rm -rf $(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER)
-# 	tar xf $(SRCDIR)/tarballs/libnetfilter_conntrack-$(LIBNFCT_VER).tar.xz -C $(SRCDIR)
-# 	cd $(SRCDIR)/libnetfilter_conntrack-$(LIBNFCT_VER) && \
-# 		git init -q && git add -A && git commit -q -m "upstream" && \
-# 		git apply $(PATCHES)/libnetfilter-conntrack/01-nxp-ask-comcerto-fp-extensions.patch && \
-# 		PKG_CONFIG_PATH=$(SYSROOT)/lib/pkgconfig \
-# 		./configure --host=$(HOST) --prefix=$(SYSROOT) --enable-static --disable-shared -q \
-# 			CFLAGS="-I$(SYSROOT)/include" LDFLAGS="-L$(SYSROOT)/lib" && \
-# 		$(MAKE) -j$$(nproc) -s && $(MAKE) install -s
-# 	@touch $@
-# 
-# modules: cdx fci auto_bridge
-# 
-# cdx:
-# 	$(MAKE) -C cdx $(CDX_ARGS) modules
-# 
-# fci: cdx
-# 	$(MAKE) -C fci $(FCI_ARGS) modules
-# 
-# auto_bridge:
-# 	$(MAKE) -C auto_bridge $(ABM_ARGS)
-# 
-# # ============================================================================
-# #  Userspace binaries
-# # ============================================================================
-# 
-# userspace: fmc cmm dpa_app
-# 
-# fmc: $(S)/fmc
-# 	@true
-# 
-# cmm: $(S)/libfci $(S)/libnfct
-# 	$(MAKE) -C cmm CC=$(CC) \
-# 		LIBFCI_DIR=$(LIBFCI_DIR) \
-# 		ABM_DIR=$(ABM_DIR) \
-# 		SYSROOT=$(SYSROOT)
-# 
-# dpa_app: $(S)/fmc
-# 	$(MAKE) -C dpa_app CC=$(CC) \
-# 		CFLAGS="-DDPAA_DEBUG_ENABLE -DNCSW_LINUX \
-# 		-I$(FMC_DIR) -I$(CURDIR)/cdx \
-# 		-I$(FMLIB_DIR)/include/fmd \
-# 		-I$(FMLIB_DIR)/include/fmd/Peripherals \
-# 		-I$(FMLIB_DIR)/include/fmd/integrations" \
-# 		LDFLAGS="-lpthread -lcli -L$(FMC_DIR) -lfmc -L$(FMLIB_DIR) -lfm -lstdc++ -lxml2 -lm"
+# fmlib, fmc
+RUN mkdir fmlib && \
+    tar zxf "fmlib.tar.gz" -C "fmlib" --strip-components=1 && \
+    cd fmlib && git init -q && git add -A && git commit -q -m "base" && \
+    git apply /src/ASK/patches/fmlib/01-mono-ask-extensions.patch && \
+    make CROSS_COMPILE=aarch64-linux-musl- KERNEL_SRC=../linux libfm-arm.a && \
+    ln -sf libfm-arm.a libfm.a && \
+    cd ../ && \
+    mkdir -p fmc && \
+    tar zxf "fmc.tar.gz" -C "fmc" --strip-components=1 && \
+    # Handle fmc: Initialize dummy repo, patch, and build \
+    cd fmc && git init -q && git add -A && git commit -q -m "base" && \
+    git apply /src/ASK/patches/fmc/01-mono-ask-extensions.patch && \
+    make -C "source" CC="aarch64-linux-musl-gcc -static" CXX="aarch64-linux-musl-g++ -static" AR=aarch64-linux-musl-ar \
+        MACHINE=ls1046 \
+        FMD_USPACE_HEADER_PATH=../../fmlib/include/fmd \
+        FMD_USPACE_LIB_PATH=../../fmlib \
+        LIBXML2_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include/libxml2 \
+        TCLAP_HEADER_PATH=/opt/aarch64-linux-musl-cross/aarch64-linux-musl/include
+
+# Common paths / flags for the remaining ASK build.
+# Keep userspace fully static against the musl sysroot.
+ENV ASK_DIR=/src/ASK \
+    KERNEL_DIR=/src/linux \
+    SYSROOT=/opt/aarch64-linux-musl-cross/aarch64-linux-musl \
+    HOST=aarch64-linux-musl \
+    CROSS_COMPILE=aarch64-linux-musl- \
+    ARCH=arm64 \
+    PLATFORM=LS1043A
+
+# Build patched libnfnetlink + libnetfilter_conntrack into the musl sysroot.
+# These are needed by cmm through pkg-config.
+RUN mkdir -p "${ASK_DIR}/sources" && \
+    mkdir -p libnfnetlink && \
+    tar xjf "${ASK_DIR}/sources/tarballs/libnfnetlink-${LIBNFNETLINK_VERSION}.tar.bz2" \
+        -C libnfnetlink --strip-components=1 && \
+    cd libnfnetlink && \
+    git init -q && git add -A && git commit -q -m "upstream" && \
+    git apply "${ASK_DIR}/patches/libnfnetlink/01-nxp-ask-nonblocking-heap-buffer.patch" && \
+    CC=aarch64-linux-musl-gcc AR=aarch64-linux-musl-ar RANLIB=aarch64-linux-musl-ranlib \
+    ./configure --host="${HOST}" --prefix="${SYSROOT}" \
+        --enable-static --disable-shared && \
+    make -j$(nproc) && make install && \
+    cd /src && rm -rf libnfnetlink && \
+    mkdir -p libnetfilter_conntrack && \
+    tar xf "${ASK_DIR}/sources/tarballs/libnetfilter_conntrack-${LIBNFCT_VERSION}.tar.xz" \
+        -C libnetfilter_conntrack --strip-components=1 && \
+    cd libnetfilter_conntrack && \
+    git init -q && git add -A && git commit -q -m "upstream" && \
+    git apply "${ASK_DIR}/patches/libnetfilter-conntrack/01-nxp-ask-comcerto-fp-extensions.patch" && \
+    PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig" \
+    CC=aarch64-linux-musl-gcc AR=aarch64-linux-musl-ar RANLIB=aarch64-linux-musl-ranlib \
+    ./configure --host="${HOST}" --prefix="${SYSROOT}" \
+        --enable-static --disable-shared \
+        CFLAGS="-I${SYSROOT}/include" \
+        LDFLAGS="-L${SYSROOT}/lib" && \
+    make -j$(nproc) && make install && \
+    cd /src && rm -rf libnetfilter_conntrack
+
+# Build libfci, used by cmm.
+RUN make -C "${ASK_DIR}/fci/lib" \
+    CC=aarch64-linux-musl-gcc \
+    AR=aarch64-linux-musl-ar
+
+COPY patches/ask/0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch /src/
+
+RUN cd /src/ASK && \
+    git init -q && git add -A && git commit -q -m "base" && \
+    git apply /src/0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch
+
+# Build the kernel
+COPY kernel-extra.config /src/kernel-extra.config
+COPY kernel-build/dts/*.dts /src/linux/arch/arm64/boot/dts/freescale/
+COPY kernel-build/ensure-kconfig.sh /src/
+
+# NXP tree: use the LSDK defconfig target
+RUN cd /src/linux \
+ && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" defconfig lsdk.config \
+ && ./scripts/kconfig/merge_config.sh -m .config "${ASK_DIR}/config/kernel/defconfig" /src/kernel-extra.config \
+ && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" olddefconfig \
+ && /src/ensure-kconfig.sh .config /src/kernel-extra.config \
+ && make ARCH="${ARCH}" CROSS_COMPILE="${CROSS_COMPILE}" -j"$(nproc)"
+
+
+# Build out-of-tree ASK kernel modules: cdx -> fci, plus auto_bridge.
+RUN make -C "${ASK_DIR}/cdx" \
+        CROSS_COMPILE="${CROSS_COMPILE}" \
+        ARCH="${ARCH}" \
+        KERNELDIR="${KERNEL_DIR}" \
+        PLATFORM="${PLATFORM}" \
+        modules && \
+    make -C "${ASK_DIR}/fci" \
+        CROSS_COMPILE="${CROSS_COMPILE}" \
+        ARCH="${ARCH}" \
+        KERNEL_SOURCE="${KERNEL_DIR}" \
+        BOARD_ARCH="${ARCH}" \
+        KBUILD_EXTRA_SYMBOLS="${ASK_DIR}/cdx/Module.symvers" \
+        modules && \
+    make -C "${ASK_DIR}/auto_bridge" \
+        CROSS_COMPILE="${CROSS_COMPILE}" \
+        ARCH="${ARCH}" \
+        KERNEL_SOURCE="${KERNEL_DIR}" \
+        PLATFORM="${PLATFORM}"
+
+# Build remaining userspace binaries: cmm and dpa_app.
+RUN make -C "${ASK_DIR}/cmm" \
+        CC="aarch64-linux-musl-gcc -static" \
+        LIBFCI_DIR="${ASK_DIR}/fci/lib" \
+        ABM_DIR="${ASK_DIR}/auto_bridge" \
+        SYSROOT="${SYSROOT}" \
+        PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig" && \
+    make -C "${ASK_DIR}/dpa_app" \
+        CC="aarch64-linux-musl-gcc -static" \
+        CFLAGS="-DDPAA_DEBUG_ENABLE -DNCSW_LINUX \
+            -I/src/fmc/source \
+            -I${ASK_DIR}/cdx \
+            -I/src/fmlib/include/fmd \
+            -I/src/fmlib/include/fmd/Peripherals \
+            -I/src/fmlib/include/fmd/integrations" \
+        LDFLAGS="-static -L/src/fmc/source -L/src/fmlib -L${SYSROOT}/lib \
+            -lfmc -lfm -lcli -lxml2 -lstdc++ -lpthread -lm"
+
+# Stage outputs.
+RUN mkdir -p /out/ask && \
+    cp "${ASK_DIR}/cdx/cdx.ko" /out/ask/ && \
+    cp "${ASK_DIR}/fci/fci.ko" /out/ask/ && \
+    cp "${ASK_DIR}/auto_bridge/auto_bridge.ko" /out/ask/ && \
+    cp /src/fmc/source/fmc /out/ask/ && \
+    cp "${ASK_DIR}/cmm/src/cmm" /out/ask/ && \
+    cp "${ASK_DIR}/dpa_app/dpa_app" /out/ask/ && \
+    aarch64-linux-musl-strip /out/ask/fmc /out/ask/cmm /out/ask/dpa_app || true
+
+FROM scratch AS ask-artifacts
+COPY --from=build /out/ask/ /
diff --git a/kernel-extra.config b/kernel-extra.config
index e221d2c..5b8ef26 100644
--- a/kernel-extra.config
+++ b/kernel-extra.config
@@ -252,4 +252,4 @@ CONFIG_TASK_XACCT=y
 # NXP
 ###############################################################################
 
-CONFIG_CDX_BUS=y
+# CONFIG_CDX_BUS=y
diff --git a/patches/ask/0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch b/patches/ask/0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch
new file mode 100644
index 0000000..f018088
--- /dev/null
+++ b/patches/ask/0001-cdx-adapt-dpa-wifi-a050385-to-6.18.patch
@@ -0,0 +1,76 @@
+--- a/cdx/dpa_wifi.c	2026-05-08 15:39:41.418608108 +0000
++++ b/cdx/dpa_wifi.c	2026-05-08 15:39:41.434536563 +0000
+@@ -840,17 +840,15 @@
+ 	/* Get a page frag to store the SGTable, or a full page if the errata
+ 	 * is in place and we need to avoid crossing a 4k boundary.
+ 	 */
+-#ifdef FM_ERRATUM_A050385
+ 	if (unlikely(fm_has_errata_a050385())) {
+ 		struct page *new_page = alloc_page(GFP_ATOMIC);
+ 
+ 		if (unlikely(!new_page))
+ 			return -ENOMEM;
+ 		sgt_buf = page_address(new_page);
+-	}
+-	else
+-#endif
++	} else {
+ 		sgt_buf = netdev_alloc_frag(priv->eth_priv->tx_headroom + sgt_size);
++	}
+ 
+ 	if (unlikely(!sgt_buf)) {
+ 		dev_err(dpa_bp->dev, "netdev_alloc_frag() failed\n");
+@@ -1082,11 +1080,7 @@
+ 		/* We do not support Jumbo frames on LS1043 and thus we edit
+ 		 * the skb truesize only when the 4k errata is not present.
+ 		 */
+-#ifdef FM_ERRATUM_A050385
+ 		if (likely(!fm_has_errata_a050385())) {
+-#else
+-		if (likely(!dpaa_errata_a010022)) {
+-#endif
+ 			skb->truesize = SKB_TRUESIZE(dpa_fd_length(fd));
+ 		}
+ 	}
+@@ -1152,11 +1146,8 @@
+ 	err = custom_vwd_skb_to_sg_fd(priv, skb, &fd);
+ 	INCR_PER_CPU_STAT(vap_dev->vap_stats, pkts_tx_sg);
+ #else
+-#ifdef FM_ERRATUM_A050385
+ 	if (unlikely(fm_has_errata_a050385()) && a050385_check_skb(skb, priv->eth_priv))
+ 		skb_need_wa = true;
+-#endif
+-
+ 
+ 	nonlinear = skb_is_nonlinear(skb);
+ 
+@@ -1217,7 +1208,6 @@
+ 			 * more fragments than we support. In this case,
+ 			 * we have no choice but to linearize it ourselves.
+ 			 */
+-#ifdef FM_ERRATUM_A050385
+ 			/* No point in linearizing the skb now if we are going
+ 			 * to realign and linearize it again further down due
+ 			 * to the A050385 errata
+@@ -1226,13 +1216,11 @@
+ 				skb_need_wa = true;
+ 			else
+ 				err = __skb_linearize(skb);
+-#endif
+ 		}
+ 		if (unlikely(!skb || err < 0))
+ 			/* Common out-of-memory error path */
+ 			goto skb_to_fd_failed;
+ 
+-#ifdef FM_ERRATUM_A050385
+ 		/* Verify the skb a second time if it has been updated since
+ 		 * the previous check
+ 		 */
+@@ -1248,7 +1236,6 @@
+ 			dev_kfree_skb(skb);
+ 			skb = nskb;
+ 		}
+-#endif
+ 
+ 		err = vwd_skb_to_contig_fd(priv, skb, &fd, &offset);
+ 	}
diff --git a/patches/ask/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch b/patches/ask/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch
new file mode 100644
index 0000000..69c897f
--- /dev/null
+++ b/patches/ask/kernel/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch
@@ -0,0 +1,71 @@
+diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h
+index 12a409d..740793d 100644
+--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h
++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth.h
+@@ -37,6 +37,7 @@
+ #include <linux/fsl_qman.h>	/* struct qman_fq */
+ 
+ #include "fm_ext.h"
++#include "fm_ehash.h"
+ #include "dpaa_eth_trace.h"
+ 
+ extern int dpa_rx_extra_headroom;
+@@ -88,8 +89,13 @@ static inline void DPA_BUG_ON(bool cond)
+ 	(FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT | \
+ 	 FM_PORT_FRM_ERR_LENGTH | FM_PORT_FRM_ERR_DMA)
+ 
+-/* The raw buffer size must be cacheline aligned. */
+-#define DPA_BP_RAW_SIZE		2048
++/* The raw buffer size must be cacheline aligned.
++ * As 1518 byte packets are received in scatter gather buffers from DPAA,
++ * and these buffers are used by Wi-Fi which requires contiguous buffers,
++ * increase the raw buffer size from 2048 to 2176 to accommodate them in a
++ * contiguous FD.
++ */
++#define DPA_BP_RAW_SIZE		2176
+ 
+ /* This is what FMan is ever allowed to use.
+  * FMan-DMA requires 16-byte alignment for Rx buffers, but SKB_DATA_ALIGN is
+@@ -174,6 +180,7 @@ static inline void DPA_BUG_ON(bool cond)
+ #endif
+ 
+ #define DPAA_ETH_RX_QUEUES	128
++#define DPAA_IP_VERSION_4	4
+ 
+ /* Convenience macros for storing/retrieving the skb back-pointers. They must
+  * accommodate both recycling and confirmation paths - i.e. cases when the buf
+@@ -304,6 +311,10 @@ struct dpa_percpu_priv_s {
+ 	u64 tx_frag_skbuffs;
+ 	/* number of S/G frames received */
+ 	u64 rx_sg;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) && defined(CONFIG_CPE_FAST_PATH)
++	u64 tx_caam_enc;
++	u64 tx_caam_dec;
++#endif
+ 
+ 	struct rtnl_link_stats64 stats;
+ 	struct dpa_rx_errors rx_errors;
+@@ -375,9 +386,13 @@ struct dpa_priv_s {
+ 	int loop_id;
+ 	int loop_to;
+ #endif
+-#ifdef CONFIG_FSL_DPAA_CEETM
++#if defined(CONFIG_FSL_DPAA_CEETM) || defined(CONFIG_CPE_FAST_PATH)
+ 	bool ceetm_en; /* CEETM QoS enabled */
++#ifdef CONFIG_CPE_FAST_PATH
++	void *qm_ctx;  /* CEETM context */
++#endif
+ #endif
++	void *ifinfo;
+ };
+ 
+ struct fm_port_fqs {
+@@ -392,7 +407,7 @@ struct fm_port_fqs {
+ extern struct net_device *dpa_loop_netdevs[20];
+ #endif
+ 
+-int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *count_ptr);
++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *count_ptr, int threshold);
+ void __hot _dpa_rx(struct net_device *net_dev,
+ 		struct qman_portal *portal,
+ 		const struct dpa_priv_s *priv,
diff --git a/patches/ask/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch b/patches/ask/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch
new file mode 100644
index 0000000..d335909
--- /dev/null
+++ b/patches/ask/kernel/0009-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth_sg.c.patch
@@ -0,0 +1,1113 @@
+diff --git a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c
+index a993a93..48d9609 100644
+--- a/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c
++++ b/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_sg.c
+@@ -52,6 +52,55 @@
+ #endif
+ #ifdef CONFIG_FSL_DPAA_CEETM
+ #include "dpaa_eth_ceetm.h"
++#endif
++#if defined(CONFIG_IP_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_MARK)
++#include "net/netfilter/nf_conntrack.h"
++#endif // CONFIG_IP_NF_CONNTRACK_MARK ||  CONFIG_NF_CONNTRACK_MARK
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++#include <uapi/linux/if_ether.h>
++#include <uapi/linux/ppp_defs.h>
++#include <uapi/linux/if_pppox.h>
++#include <net/xfrm.h>
++#endif
++
++#define DPAA_EXTRA_BUF_SIZE_4_SKB SMP_CACHE_BYTES + DPA_MAX_FD_OFFSET + \
++		sizeof(struct skb_shared_info) +  128 
++
++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD
++//added for IPR offload to FMAN
++static dpaa_eth_bpool_replenish_hook_t dpaa_eth_bpool_replenish_hook;
++#endif
++
++/* registered function to get ceetm Fqs */
++#ifdef CONFIG_CPE_FAST_PATH
++static cdx_get_ceetm_egressfq ceetm_fqget_func;
++static cdx_get_ceetm_dscp_fq ceetm_dscp_fqget_func;
++/* This function registers two cdx functions, which are to get the egress fq. 
++   One is based on channel and classqueue and another one is based on dscp. */
++int dpa_register_ceetm_get_egress_fq(cdx_get_ceetm_egressfq egress_fq_func, cdx_get_ceetm_dscp_fq dscp_fq_func)
++{
++	ceetm_fqget_func = egress_fq_func;
++	ceetm_dscp_fqget_func = dscp_fq_func;
++	return 0;
++}
++EXPORT_SYMBOL(dpa_register_ceetm_get_egress_fq);
++#endif
++
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++cdx_get_ipsec_fq_hook_t cdx_get_ipsec_fq_hookfn;
++
++int dpa_register_ipsec_fq_handler(cdx_get_ipsec_fq_hook_t hookfn)
++{
++       if (cdx_get_ipsec_fq_hookfn) {
++               printk("%s::hook already registered\n", __func__);
++               return -1;
++       }
++       cdx_get_ipsec_fq_hookfn = hookfn;
++       return 0;
++}
++
++EXPORT_SYMBOL(dpa_register_ipsec_fq_handler);
++
+ #endif
+ 
+ /* DMA map and add a page frag back into the bpool.
+@@ -81,6 +130,109 @@ static void dpa_bp_recycle_frag(struct dpa_bp *dpa_bp, unsigned long vaddr,
+ 	(*count_ptr)++;
+ }
+ 
++// adding a new function which allocates memory for buffers and adds bman pool
++int dpaa_bp_alloc_n_add_buffs(const struct dpa_bp *dpa_bp, uint32_t nbuffs, bool act_skb)
++{
++	struct bm_buffer bmb[8];
++	char *new_buf, *ptr_buf;
++	dma_addr_t addr;
++	int ii,entries_in_bmb,buffs2fill;
++	struct device *dev = dpa_bp->dev;
++	struct sk_buff *skb = NULL, **skbh;
++	uint32_t bufsize;
++
++	if (act_skb)
++		bufsize =  DPAA_EXTRA_BUF_SIZE_4_SKB + dpa_bp->size;
++	else
++		bufsize =  SMP_CACHE_BYTES + dpa_bp->size + DPA_MAX_FD_OFFSET + 128;
++
++	/* aligning to SMP_CACHE_BYTES */
++	bufsize = DPA_SKB_SIZE(bufsize);
++
++	buffs2fill= 0;
++	while (buffs2fill < nbuffs)
++	{
++		memset(bmb, 0, sizeof(struct bm_buffer) * 8);
++
++		if ((nbuffs -   buffs2fill) < 8)
++			entries_in_bmb = (nbuffs - buffs2fill);
++		else
++			entries_in_bmb = 8;
++		for (ii= 0; ii<entries_in_bmb; ii++) {
++			ptr_buf = (char *)kmalloc(bufsize, GFP_DMA | GFP_ATOMIC);
++
++			if (unlikely(!ptr_buf))
++			{
++				pr_err("%s(%d) ii %d,  buffs2fill %d kmalloc failed \n",
++						__func__,__LINE__,ii, buffs2fill);
++				goto handle_fail;
++			}
++
++			new_buf = ptr_buf;
++			if (act_skb)
++			{
++				/* We'll prepend the skb back-pointer; can't use the DPA
++				 * priv space, because FMan will overwrite it (from offset 0)
++				 * if it ends up being the second, third, etc. fragment
++				 * in a S/G frame.
++				 *
++				 * We only need enough space to store a pointer, but allocate
++				 * an entire cacheline for performance reasons.
++				 */
++				skb = build_skb(ptr_buf, 0);
++				if (unlikely(!skb)) {
++					pr_err("%s(%d) ii %d , buffs2fill %d build_skb failed \n",
++							__func__,__LINE__, ii, buffs2fill);
++					kfree(new_buf);
++					goto handle_fail;
++				}
++				ptr_buf = PTR_ALIGN(ptr_buf, SMP_CACHE_BYTES) + SMP_CACHE_BYTES;
++				skb_reserve(skb, (ptr_buf-new_buf));
++				DPA_WRITE_SKB_PTR(skb, skbh, ptr_buf, -1);
++			}
++			else
++				ptr_buf = PTR_ALIGN(ptr_buf /*+ SMP_CACHE_BYTES */, SMP_CACHE_BYTES);
++
++			addr = dma_map_single(dev, ptr_buf, dpa_bp->size, DMA_BIDIRECTIONAL);
++			if (unlikely(dma_mapping_error(dev, addr)))
++			{
++				pr_err("%s(%d) ii %d buffs2fill %d dma_mapping_error failed \n",
++						__func__,__LINE__,ii,buffs2fill);
++				if (act_skb)
++				{
++					kfree_skb(skb);
++				}
++				kfree(new_buf);
++				goto handle_fail;
++			}
++			bm_buffer_set64(&bmb[ii], addr);
++
++			bmb[ii].bpid = cpu_to_be16(dpa_bp->bpid & 0xff);
++		}
++		while (unlikely(bman_release(dpa_bp->pool, bmb, entries_in_bmb, 0)))
++			cpu_relax();
++		buffs2fill+= entries_in_bmb;
++		/*     printk("%s(%d) adrsses %lx %lx %lx %lx %lx %lx %lx %lx\n",
++			   __func__,__LINE__,bm_buffer_get64(&bmb[0]),bm_buffer_get64(&bmb[1]),
++			   bm_buffer_get64(&bmb[2]),bm_buffer_get64(&bmb[3]),bm_buffer_get64(&bmb[4]),
++			   bm_buffer_get64(&bmb[5]),bm_buffer_get64(&bmb[6]),bm_buffer_get64(&bmb[7])); */
++	}
++	/* printk("%s(%d) buff allocation and bman release success buffs2fill %d\n",
++	   __func__,__LINE__,buffs2fill); */
++	return 0;
++
++handle_fail:
++	if (ii)
++	{
++		while (unlikely(bman_release(dpa_bp->pool, bmb, ii, 0)))
++			cpu_relax();
++	}
++	return ii+buffs2fill;
++
++}
++EXPORT_SYMBOL(dpaa_bp_alloc_n_add_buffs);
++
++
+ static int _dpa_bp_add_8_bufs(const struct dpa_bp *dpa_bp)
+ {
+ 	void *new_buf, *fman_buf;
+@@ -189,12 +341,13 @@ err_release_previous_bufs:
+ /* Add buffers/(pages) for Rx processing whenever bpool count falls below
+  * REFILL_THRESHOLD.
+  */
+-int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr)
++int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr, int threshold)
+ {
+ 	int count = *countptr;
+ 	int new_bufs;
++	int percpu_buf_cfg_count = dpa_bp->config_count;
+ 
+-	if (unlikely(count < CONFIG_FSL_DPAA_ETH_REFILL_THRESHOLD)) {
++	if (unlikely(count <= (percpu_buf_cfg_count - threshold))) {
+ 		do {
+ 			new_bufs = _dpa_bp_add_8_bufs(dpa_bp);
+ 			if (unlikely(!new_bufs)) {
+@@ -205,10 +358,10 @@ int dpaa_eth_refill_bpools(struct dpa_bp *dpa_bp, int *countptr)
+ 				break;
+ 			}
+ 			count += new_bufs;
+-		} while (count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT);
++		} while (count < percpu_buf_cfg_count);
+ 
+ 		*countptr = count;
+-		if (unlikely(count < CONFIG_FSL_DPAA_ETH_MAX_BUF_COUNT))
++		if (unlikely(count < percpu_buf_cfg_count))
+ 			return -ENOMEM;
+ 	}
+ 
+@@ -372,7 +525,7 @@ EXPORT_SYMBOL(dpa_buf_is_recyclable);
+  * We are guaranteed there is enough room at the end of the data buffer to
+  * accommodate the shared info area of the skb.
+  */
+-static struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv,
++struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv,
+ 					      const struct qm_fd *fd,
+ 					      bool *use_gro, bool dcl4c_valid)
+ {
+@@ -410,14 +563,14 @@ static struct sk_buff *__hot contig_fd_to_skb(const struct dpa_priv_s *priv,
+ 
+ 	return skb;
+ }
+-
++EXPORT_SYMBOL(contig_fd_to_skb);
+ 
+ /* Build an skb with the data of the first S/G entry in the linear portion and
+  * the rest of the frame as skb fragments.
+  *
+  * The page fragment holding the S/G Table is recycled here.
+  */
+-static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv,
++struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv,
+ 					  const struct qm_fd *fd, bool *use_gro,
+ 					  int *count_ptr, bool dcl4c_valid)
+ {
+@@ -437,7 +590,8 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv,
+ 	vaddr = phys_to_virt(addr);
+ 	DPA_BUG_ON(!IS_ALIGNED((unsigned long)vaddr, SMP_CACHE_BYTES));
+ 
+-	dpa_bp = priv->dpa_bp;
++/*	dpa_bp = priv->dpa_bp; */
++	dpa_bp =  dpa_bpid2pool(fd->bpid);
+ 	/* Iterate through the SGT entries and add data buffers to the skb */
+ 	sgt = vaddr + fd_off;
+ 	for (i = 0; i < DPA_SGT_MAX_ENTRIES; i++) {
+@@ -445,9 +599,9 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv,
+ 		DPA_BUG_ON(qm_sg_entry_get_ext(&sgt[i]));
+ 
+ 		/* We use a single global Rx pool */
+-		DPA_BUG_ON(dpa_bp !=
++/*		DPA_BUG_ON(dpa_bp !=
+ 			   dpa_bpid2pool(qm_sg_entry_get_bpid(&sgt[i])));
+-
++*/
+ 		sg_addr = qm_sg_addr(&sgt[i]);
+ 		sg_vaddr = phys_to_virt(sg_addr);
+ 		DPA_BUG_ON(!IS_ALIGNED((unsigned long)sg_vaddr,
+@@ -530,6 +684,7 @@ static struct sk_buff *__hot sg_fd_to_skb(const struct dpa_priv_s *priv,
+ 	dpa_bp_recycle_frag(dpa_bp, (unsigned long)vaddr, count_ptr);
+ 	return skb;
+ }
++EXPORT_SYMBOL(sg_fd_to_skb);
+ 
+ #ifdef CONFIG_FSL_DPAA_DBG_LOOP
+ static inline int dpa_skb_loop(const struct dpa_priv_s *priv,
+@@ -547,6 +702,14 @@ static inline int dpa_skb_loop(const struct dpa_priv_s *priv,
+ }
+ #endif
+ 
++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD
++void register_dpaa_eth_bpool_replenish_hook(dpaa_eth_bpool_replenish_hook_t func)
++{
++	dpaa_eth_bpool_replenish_hook = func;
++}
++EXPORT_SYMBOL(register_dpaa_eth_bpool_replenish_hook);
++#endif
++
+ void __hot _dpa_rx(struct net_device *net_dev,
+ 		struct qman_portal *portal,
+ 		const struct dpa_priv_s *priv,
+@@ -566,7 +729,7 @@ void __hot _dpa_rx(struct net_device *net_dev,
+ 
+ 	if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) {
+ 		if (netif_msg_hw(priv) && net_ratelimit())
+-			netdev_warn(net_dev, "FD status = 0x%08x\n",
++			netdev_warn(net_dev, "_dpa_rx FD status = 0x%08x\n",
+ 					fd_status & FM_FD_STAT_RX_ERRORS);
+ 
+ 		percpu_stats->rx_errors++;
+@@ -592,6 +755,15 @@ void __hot _dpa_rx(struct net_device *net_dev,
+ 			/* won't count the rx bytes in */
+ 			return;
+ 		}
++#endif
++#ifndef EXCLUDE_FMAN_IPR_OFFLOAD
++		if (dpaa_eth_bpool_replenish_hook) {
++			if (dpaa_eth_bpool_replenish_hook(net_dev, fd->bpid)) {
++				//could not replenish buffer, drop packet ??
++				printk("%s::could not replenish buffer to pool\n",
++						__func__);
++			}
++		}
+ #endif
+ 		skb = contig_fd_to_skb(priv, fd, &use_gro, dcl4c_valid);
+ 	} else {
+@@ -638,6 +810,7 @@ void __hot _dpa_rx(struct net_device *net_dev,
+ _release_frame:
+ 	dpa_fd_release(net_dev, fd);
+ }
++EXPORT_SYMBOL(_dpa_rx);
+ 
+ int __hot skb_to_contig_fd(struct dpa_priv_s *priv,
+ 			   struct sk_buff *skb, struct qm_fd *fd,
+@@ -738,7 +911,7 @@ EXPORT_SYMBOL(skb_to_contig_fd);
+  * - SG fragments that aren't mod 16 bytes in size (except for the last
+  *   fragment)
+  */
+-static bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv)
++bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv)
+ {
+ 	skb_frag_t *frag;
+ 	int i, nr_frags;
+@@ -800,12 +973,13 @@ static bool a050385_check_skb(struct sk_buff *skb, struct dpa_priv_s *priv)
+ 
+ 	return false;
+ }
++EXPORT_SYMBOL(a050385_check_skb);
+ 
+ /* Realign the skb by copying its contents at the start of a newly allocated
+  * page. Build a new skb around the new buffer and release the old one.
+  * A performance drop should be expected.
+  */
+-static struct sk_buff *a050385_realign_skb(struct sk_buff *skb,
++struct sk_buff *a050385_realign_skb(struct sk_buff *skb,
+ 					   struct dpa_priv_s *priv)
+ {
+ 	int trans_offset = skb_transport_offset(skb);
+@@ -874,7 +1048,212 @@ err:
+ 	put_page(npage);
+ 	return NULL;
+ }
++EXPORT_SYMBOL(a050385_realign_skb);
++
++struct dpa_bp *sg_bpool_g;
++EXPORT_SYMBOL(sg_bpool_g);
++
++struct dpa_bp *skb_2bfreed_bpool_g; //if no recyclable skbs exist in skb fraglist, those should be freed back, SEC engine will add to this bman pool
++EXPORT_SYMBOL(skb_2bfreed_bpool_g);
++
++struct device* dpa_get_bp_device(void)
++{
++	if (skb_2bfreed_bpool_g)
++		return skb_2bfreed_bpool_g->dev;
++	else
++		return NULL;
++}
++
++/* 
++ *This functions unmaps all the mapped skb sg so far and stored in sgt.  
++*/
++static void dma_unmap_skb_sg_addrs(struct device *dev, struct sk_buff *skb,
++			struct qm_sg_entry *sgt, int skb_list_frags)
++{
++	dma_addr_t addr;
++	struct sk_buff *list_skb;
++	struct sk_buff *cur_skb;
++	const enum dma_data_direction dma_dir = DMA_TO_DEVICE;
++	int sgt_idx = 0;
++	int nr_frags = 0;
++
+ 
++	for (cur_skb = skb, list_skb = skb_shinfo(skb)->frag_list; 
++		(sgt_idx < skb_list_frags) && (cur_skb); )
++	{
++		addr = qm_sg_addr(&sgt[sgt_idx]);
++		dma_unmap_single(dev, addr, qm_sg_entry_get_len(&sgt[sgt_idx]), dma_dir);
++		sgt_idx++;
++
++		nr_frags = skb_shinfo(cur_skb)->nr_frags;
++		while(nr_frags)
++		{
++			addr = qm_sg_addr(&sgt[sgt_idx]);
++			dma_unmap_page(dev, addr, qm_sg_entry_get_len(&sgt[sgt_idx]), dma_dir);
++			nr_frags--;
++			sgt_idx++;
++		}
++		cur_skb = list_skb;
++		if (list_skb)
++			list_skb = list_skb->next;
++	}
++
++	return;
++}
++
++/*
++ * This inline function updates the sgt entry parameters.
++ */
++static inline void prepare_qm_sg_entry(struct qm_sg_entry *sgt, int sgt_idx, 
++					unsigned int len, dma_addr_t addr)
++{
++	qm_sg_entry_set_ext(&sgt[sgt_idx], 0);
++	qm_sg_entry_set_final(&sgt[sgt_idx], 0);
++	/* fill the first entry with skb details */
++	qm_sg_entry_set_bpid(&sgt[sgt_idx], 0xff);
++	/* set the offset and addr pointers such that
++	   in next alloc, that addr should point to skb*/
++	qm_sg_entry_set_offset(&sgt[sgt_idx],0);
++	qm_sg_entry_set_len(&sgt[sgt_idx], len);
++	qm_sg_entry_set64(&sgt[sgt_idx], addr);
++
++	return;
++}
++
++int __hot skb_fraglist_to_sg_fd(struct device *dev, struct net_device *net_dev , struct sk_buff *skb, struct qm_fd *fd, u32 fd_cmd)
++{
++	dma_addr_t addr, sg_addr;
++	struct sk_buff *list_skb;
++	struct sk_buff *cur_skb;
++	int  sgt_size;
++	int err;
++	struct qm_sg_entry *sgt;
++	struct bm_buffer bmb;
++	int sgt_index = 0, nr_frags = 0;
++	const enum dma_data_direction dma_dir = DMA_TO_DEVICE;
++	int dma_map_size;
++	skb_frag_t *frag;
++
++	// calculate the number of fragments
++	// check if any of skb is not recyclable
++	nr_frags = 1 + skb_shinfo(skb)->nr_frags;
++	skb_walk_frags(skb, list_skb)
++	{
++		nr_frags += 1 + skb_shinfo(list_skb)->nr_frags;
++	}
++
++	/* The below condition covers nr_frags + 1(This is to store skb address in opaque variable at the end.)*/
++	if (nr_frags > (DPA_SGT_MAX_ENTRIES))
++	{
++		/* TODO linearize */
++		pr_err("%s()::%d number of skb frags %d crossed the MAX SGT entries%d", __func__, __LINE__, nr_frags, DPA_SGT_MAX_ENTRIES);
++		return -1;
++	}
++
++	fd->format = qm_fd_sg;
++	/* allocate an SG buffer from SG BMAN POOL
++	   if any entries in skb_2bfreed_bpool_g, acquire and free skb and use the SG buffer */
++	if (bman_acquire(skb_2bfreed_bpool_g->pool, &bmb, 1, 0) == 1)
++	{
++		sgt =  (struct qm_sg_entry *)phys_to_virt(bmb.addr);
++		while (!qm_sg_entry_get_final(&sgt[sgt_index]))
++			sgt_index++;
++		addr = sgt[sgt_index+1].opaque;
++		if (addr) /* free the old skb */
++		{
++			list_skb =  (struct sk_buff *)(addr);
++			kfree_skb(list_skb);
++		}
++		sgt[sgt_index+1].opaque = 0; /* set to 0*/
++	}
++	else if (bman_acquire(sg_bpool_g->pool, &bmb, 1, 0) == 1)
++	{
++		sgt =  (struct qm_sg_entry *)phys_to_virt(bmb.addr);
++	} 
++	else
++	{
++		printk("%s(%d) bman_acquire of SG bman buffer failed\n",
++				__func__,__LINE__);
++		return -1;
++	}
++
++
++	/* net_crit_ratelimited("%s(%d) nr_frags %d, MAX frags %d \n", __func__,__LINE__,nr_frags,DPA_SGT_MAX_ENTRIES);*/
++	/* After nr_frags, in next index of sgt array storing skb address in opaque. So doing memset for (nr_frags+1).*/
++	sgt_size = sizeof(struct qm_sg_entry) * (nr_frags + 1);
++
++
++	/* memset to 0s of size sgt_size*/
++	memset(sgt, 0, sgt_size);
++
++	
++
++	sgt_index = 0;
++	for (cur_skb = skb, list_skb = skb_shinfo(skb)->frag_list; cur_skb;)
++	{
++		dma_map_size = skb_headlen(cur_skb);
++		addr = dma_map_single(dev, cur_skb->data, dma_map_size, dma_dir);
++		if (unlikely(dma_mapping_error(dev, addr))) {
++			if (net_ratelimit())
++				netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error");
++			err = -EINVAL;
++			dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index);
++			bman_release(sg_bpool_g->pool, &bmb, 1, 0);
++			return -1;
++		}
++		prepare_qm_sg_entry(sgt, sgt_index, dma_map_size, addr);
++		sgt_index++;
++		
++		nr_frags = skb_shinfo(cur_skb)->nr_frags;
++		frag = skb_shinfo(cur_skb)->frags;
++		while(nr_frags)
++		{
++			addr = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), dma_dir);
++			if (unlikely(dma_mapping_error(dev, addr))) {
++				if (net_ratelimit())
++					netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error");
++				dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index);
++				err = -EINVAL;
++				bman_release(sg_bpool_g->pool, &bmb, 1, 0);
++				return -1;
++			}
++			prepare_qm_sg_entry(sgt, sgt_index, skb_frag_size(frag), addr);
++			frag++;
++			nr_frags--;
++			sgt_index++;
++		}
++		cur_skb = list_skb;
++		if (list_skb)
++			list_skb = list_skb->next;
++	}
++
++	qm_sg_entry_set_final(&sgt[sgt_index-1], 1);
++
++	addr = (dma_addr_t) (skb);
++	sgt[sgt_index].opaque = addr;
++
++	sg_addr = dma_map_single(dev, sgt, sgt_size, dma_dir);
++	if (unlikely(dma_mapping_error(dev, sg_addr))) {
++		/*if (netif_msg_tx_err(priv) && net_ratelimit())*/
++		dma_unmap_skb_sg_addrs(dev, skb, sgt, sgt_index);
++		if (net_ratelimit())
++			netdev_err(net_dev, "skb_fraglist_to_sg_fd : 2.DMA mappping error");
++		err = -EINVAL;
++		bman_release(sg_bpool_g->pool, &bmb, 1, 0);
++		return -1;
++	}
++	qm_fd_addr_set64(fd, sg_addr);
++	fd->bpid = skb_2bfreed_bpool_g->bpid; /*sg_bpool_g->bpid;*/
++	fd->cmd = fd_cmd;
++	fd->length20 = skb->len;
++	fd->offset = 0;
++
++	/*net_crit_ratelimited("%s(%d) sgt_index %d, addr %p \n",__func__, __LINE__,sgt_index,(void *)(sgt[sgt_index].opaque)); */
++	/* percpu_priv->tx_returned++; */
++	return 0;
++}
++
++EXPORT_SYMBOL(skb_fraglist_to_sg_fd);
+ int __hot skb_to_sg_fd(struct dpa_priv_s *priv,
+ 		       struct sk_buff *skb, struct qm_fd *fd)
+ {
+@@ -1019,9 +1398,532 @@ csum_failed:
+ }
+ EXPORT_SYMBOL(skb_to_sg_fd);
+ 
+-int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev)
++#define EMAC_QUEUENUM_MASK 0xff
++
++#ifdef CONFIG_CPE_FAST_PATH
++static int pfe_eth_get_queuenum( struct sk_buff *skb )
++{
++
++#if defined(CONFIG_IP_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_MARK)
++	/* Use conntrack mark (if conntrack exists) */
++	if (skb->_nfct) {
++		enum ip_conntrack_info cinfo;
++		struct nf_conn *ct;
++
++		ct = nf_ct_get(skb, &cinfo);
++		if (ct) {
++			u_int64_t markval;
++
++			markval = ct->qosconnmark;
++			if (cinfo >= IP_CT_IS_REPLY) {
++				if (markval & ((uint64_t)1 << 63))
++					markval >>= 32;
++               else
++					markval = 0;
++			}
++			markval &= 0x7fffffff;
++			return markval;
++
++		}
++	}
++#endif
++	/* use  packet mark (if any) */
++	if (skb->mark) {
++		return (skb->mark & EMAC_QUEUENUM_MASK);
++	}
++	/* These are packets through control path, assign highest priority */
++	return (QOS_DEFAULT_QUEUE);
++}
++#endif /*endif for CONFIG_CPE_FAST_PATH */
++
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++unsigned int ipsec_offload_pkt_cnt;
++void print_ipsec_offload_pkt_count(void)
++{
++	printk("%s:: Ipsec offload slow path packet count = %d\n",__func__,ipsec_offload_pkt_cnt);
++	ipsec_offload_pkt_cnt = 0;
++}
++EXPORT_SYMBOL(print_ipsec_offload_pkt_count);
++#endif
++
++#ifdef CONFIG_XFRM
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++
++#define ETH_HDR_SIZE            14
++#define VLAN_HDR_SIZE           4
++#define PPPOE_HDR_SIZE          8
++#define ETHHDR_MACEND_OFFSET    12
++
++#define DPOVRD_ENABLE		0x80000000
++
++unsigned char* dpa_get_skb_nh(struct sk_buff* skb, unsigned short *l3_proto, unsigned short* l3_offset)
++{
++	unsigned short type_id;
++	unsigned short offset = ETH_HDR_SIZE;
++	type_id = *((unsigned short*) (skb->data + offset - 2));
++	while (1)
++	{
++		switch (type_id)
++		{
++			case htons(ETH_P_PPP_SES):
++				offset += PPPOE_HDR_SIZE;
++				type_id = *(unsigned short*)(skb->data + offset - 2);
++
++				if (type_id == htons(PPP_IP))
++					type_id = ntohs(ETH_P_IP);
++				else if (type_id == htons(PPP_IPV6))
++					type_id = ntohs(ETH_P_IPV6);
++				break;
++			case htons(ETH_P_8021Q):
++				offset += VLAN_HDR_SIZE;
++				type_id = *(unsigned short*) (skb->data + offset - 2);
++				break;
++			case htons(ETH_P_IP):
++			case htons(ETH_P_IPV6):
++				*l3_proto = type_id;
++				*l3_offset = offset;
++				return (skb->data + offset);
++			default:
++				*l3_proto = type_id;
++				*l3_offset = offset;
++				return NULL;
++		}
++	}
++}
++
++ __hot void dpaa_submit_outb_pkt_to_SEC(struct sk_buff *skb, struct net_device *net_dev, struct dpa_bp *dpa_bp)
++{
++	struct xfrm_state *x;
++	struct qman_fq *egress_fq = NULL;
++	struct qm_fd		 fd;
++	int err = 0,ii;
++	int new_ethhdr_end, old_ethhdr_end;
++	unsigned short l3_proto, l3_offset;
++	unsigned char* skb_nh ;
++	struct sec_path *sp;
++	unsigned int dpovrd = 0;
++
++	sp = skb_sec_path(skb);
++
++	if ((cdx_get_ipsec_fq_hookfn) && (sp) && (sp->len))
++	{
++		x = sp->xvec[0];
++
++		egress_fq = cdx_get_ipsec_fq_hookfn(x->handle);
++
++		/* when a packet is submitting to SEC engine,
++		use the below code */
++		if (!egress_fq)
++		{
++			printk("%s(%d) egress frame queue not found \n",
++			__func__, __LINE__);
++			goto sec_submit_failed;	
++		}
++
++		/*
++		* For packets with L2 headers other than ethernet header, L2 headers are
++		* stripped off and only ethernet header is maintained. These L2 headers
++		* will be added again in offline port classification table entry
++		* after SEC processing.
++		*/
++
++		skb_nh = dpa_get_skb_nh(skb, &l3_proto, &l3_offset);
++
++		if (skb_nh)
++		{
++			if(l3_offset != ETH_HDR_SIZE)
++			{
++				new_ethhdr_end = l3_offset - 2  ; /*l3 offset - protocol(2bytes) */
++				old_ethhdr_end = ETHHDR_MACEND_OFFSET ;
++				while(old_ethhdr_end >= 0)
++					skb->data[new_ethhdr_end--] = skb->data[old_ethhdr_end--];
++
++				*((unsigned short*) (skb->data + l3_offset - 2) ) = l3_proto;
++				skb->data = skb->data + l3_offset - ETH_HDR_SIZE;
++				skb->len = skb->len - l3_offset + ETH_HDR_SIZE;
++			}
++			if ((*skb_nh >> 4) == IPVERSION) /* Extracting ip version from network header */
++			{
++				dpovrd = IPPROTO_IPIP; /* ESP trailer NH type. Here traffic is IPv4 */
++			}
++			else
++			{
++				dpovrd = IPPROTO_IPV6; /* ESP trailer NH type. Here traffic is IPv4 */
++			}
++			dpovrd |= DPOVRD_ENABLE; /* OVRD enable */
++		}
++
++
++		err =  skb_fraglist_to_sg_fd(dpa_bp->dev, net_dev, skb, &fd, dpovrd);
++		if (unlikely(err < 0))
++		{
++			printk("%s(%d) skb_fraglist_to_sg_fd \n",__func__,__LINE__);
++			goto sec_submit_failed;
++		}
++
++
++		for (ii = 0; ii < 100000; ii++) {
++			err = qman_enqueue(egress_fq, &fd, 0);
++			if (err != -EBUSY)
++				break;
++		}
++
++		if (unlikely(err < 0)) {
++			struct bm_buffer bmb;
++			dma_addr_t addr;
++
++			printk("%s(%d) dpa_xmit failed\n",__func__,__LINE__);
++			/* put the buffer back to sg pool */
++			addr = qm_fd_addr(&fd);
++			bm_buffer_set64(&bmb, addr);
++			while (bman_release(sg_bpool_g->pool, &bmb, 1, 0))
++				cpu_relax();
++			/* release skb and retuen */
++			goto sec_submit_failed;
++		}
++
++		/* sucessful transmit to CAAM */
++		netif_trans_update(net_dev);
++		ipsec_offload_pkt_cnt++;
++		return;
++	}
++sec_submit_failed:
++	dev_kfree_skb(skb);
++	return;
++
++}
++
++EXPORT_SYMBOL(dpaa_submit_outb_pkt_to_SEC);
++
++/**
++ * dpa_add_dummy_eth_hdr - Add dummy Ethernet header space to SKB
++ * @skb_in: Pointer to SKB pointer (may be reallocated)
++ * @priv_headroom: Private headroom to reserve (not used, for API compatibility)
++ * @hdroom_realloc: Set to 1 if headroom was reallocated
++ *
++ * This function ensures there is space for an Ethernet header in the SKB
++ * headroom for cellular interfaces that don't have a MAC header.
++ * Returns 0 on success, negative on failure.
++ */
++int dpa_add_dummy_eth_hdr(struct sk_buff **skb_in, int priv_headroom,
++			  unsigned char *hdroom_realloc)
++{
++	struct sk_buff *skb = *skb_in;
++	struct ethhdr *eth;
++	__be16 proto;
++	int headroom_needed = ETH_HLEN + priv_headroom;
++	int ipv;
++
++	/* Check if we have enough headroom */
++	if (skb_headroom(skb) < headroom_needed) {
++		struct sk_buff *new_skb;
++
++		new_skb = skb_realloc_headroom(skb, headroom_needed);
++		if (!new_skb)
++			return -ENOMEM;
++
++		dev_kfree_skb(skb);
++		*skb_in = new_skb;
++		skb = new_skb;
++		*hdroom_realloc = 1;
++	}
++
++	/* Determine IP version from first byte of packet */
++	ipv = (skb->data[0] >> 4) & 0xf;
++
++	/* Set protocol based on IP version */
++	if (ipv == 6)
++		proto = htons(ETH_P_IPV6);
++	else
++		proto = htons(ETH_P_IP);
++
++	/* Push ethernet header space */
++	eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
++
++	/* Initialize with dummy MAC addresses (will be overwritten later) */
++	memset(eth->h_dest, 0, ETH_ALEN);
++	memset(eth->h_source, 0, ETH_ALEN);
++	eth->h_proto = proto;
++
++	/* Reset SKB pointers - caller will adjust data/len */
++	skb_pull(skb, ETH_HLEN);
++
++	return 0;
++}
++EXPORT_SYMBOL(dpa_add_dummy_eth_hdr);
++
++#ifdef UNIT_TEST
++unsigned char temp_ethhdr[16];
++#endif
++/* This function is invoked from xfrm_input.c to submit IPSEC packets
++   to SEC engine, if the corresponding SA is programmed in ucode
++*/
++int dpaa_submit_inb_pkt_to_SEC(struct sk_buff *skb, uint16_t sagd)
++{
++	struct qman_fq *sec_fq = NULL;
++	struct dpa_priv_s	*priv = NULL;
++	struct dpa_percpu_priv_s *percpu_priv = NULL;
++	struct qm_fd		 fd;
++	int err, ii, ret;
++	uint8_t tmp_len = 0, data[ETH_HLEN],ipvsn;
++	struct net_device *netdev;
++	struct device* dev;
++	unsigned char hdroom_realloced = 0;
++#ifdef UNIT_TEST
++	unsigned short dev_type = skb->dev->type;
++#endif
++	struct net_device *real_dev = NULL;
++
++	if (cdx_get_ipsec_fq_hookfn)
++	{
++		/*net_crit_ratelimited("%s(%d) x->handle %d, skb->data %p, machdr %p\n",
++		  __func__,__LINE__, sagd,skb->data, skb_mac_header(skb)); */
++		/* Get the SEC FQID corresponding to sagd */
++		sec_fq = cdx_get_ipsec_fq_hookfn(sagd);
++
++		/*if no SEC FQID found, return non-zero to return pkt to linux*/
++		if (!sec_fq || !(skb->dev))
++		{
++			return -1; /* give packet to linux */
++		}
++#ifdef UNIT_TEST
++		{
++			skb->dev->type = ARPHRD_NONE;
++			skb->mac_len = 0;
++		}
++#endif
++		if (skb->dev->type == ARPHRD_ETHER) 
++		{
++			/* Get the corresponding physical device info, if the skb->dev corresponds to vlan*/
++			real_dev = is_vlan_dev(skb->dev) ? vlan_dev_real_dev(skb->dev) : skb->dev;
++			priv = netdev_priv(real_dev);
++			/* SEC shared descriptor designed to take packet including MAC hdr */
++			/* modify skb->data to point to MAC hdr */
++			tmp_len = (skb->data - skb_mac_header(skb));
++			skb->len += tmp_len;
++			skb->data = skb_mac_header(skb);
++		}
++		else if (skb->dev->type == ARPHRD_PPP)
++		{
++			/* Get the corresponding physica device info */
++			netdev = __dev_get_by_index(dev_net(skb->dev), skb->iif_index);
++			if (!netdev)
++			{
++				return -1;
++			}
++			priv = netdev_priv(netdev);
++			/* observation in case of PPPoE skb_mac_header is pointing to IP header*/
++			/* get the IP version*/
++			/*printk("%s(%d), MAC header ptr: \n",
++			  __func__,__LINE__);
++			  display_buf_data(&skb->head[skb->mac_header] ,16);*/
++			ipvsn = ((skb->head[skb->mac_header]) & 0xf0) >> 4;
++			/*printk(KERN_INFO "iif_dev :%s, ifindex %d, ipvsn %d\n", netdev->name, netdev->ifindex, ipvsn);*/
++			/* SEC shared descriptor designed to take packet including MAC hdr */
++			/* modify skb->data to point to MAC hdr */
++			tmp_len = (skb->data - skb_mac_header(skb) + ETH_HLEN);
++			skb->len += tmp_len;
++			skb->data = skb_mac_header(skb) - ETH_HLEN;
++			/* Copy MAC hdr to the skb->data */
++			/* have a backup data in case of failure */
++			memcpy(data, skb->data, ETH_HLEN);
++			/* copying 8 bytes and then 4 bytes from the backup , as there is overlap*/
++			memcpy(skb->data, skb->data-PPPOE_SES_HLEN, PPPOE_SES_HLEN);
++			memcpy(skb->data+PPPOE_SES_HLEN, data, 4);
++			/* setting IPv4/IPv6 ethernet protocol value */
++			if (ipvsn == DPAA_IP_VERSION_4)
++			{
++				skb->data[12] = 0x08;
++				skb->data[13] = 0x00;
++			}
++			else /* IP v6 */
++			{
++				skb->data[12] = 0x86;
++				skb->data[13] = 0xdd;
++			}
++			/*printk("%s(%d), modified header : \n",
++			  __func__,__LINE__);
++			  display_buf_data(skb->data,16);*/
++		}
++
++		if (skb->dev->wifi_offload_dev)
++		{
++			dev = dpa_get_bp_device();
++			netdev = skb->dev;
++			/* Following code is added for cellular interfaces */
++			/* FIX :May be we can check for device type as ARPHRD_NONE */
++			if (skb->mac_len == 0)
++			{
++				tmp_len = (skb->data - skb_network_header(skb));
++				skb->len += tmp_len;
++				skb->data = skb_network_header(skb);
++#ifdef UNIT_TEST
++				memcpy(temp_ethhdr, (skb->data - ETH_HLEN), 12);
++#endif
++				/* Add mac_header */
++				ret = dpa_add_dummy_eth_hdr(&skb, 0, &hdroom_realloced);
++				if (ret < 0)
++					return -1;
++
++				skb->data -= ETH_HLEN;
++				skb->len += ETH_HLEN;
++				tmp_len += ETH_HLEN;
++#ifdef UNIT_TEST
++				memcpy(skb->data ,temp_ethhdr,12);
++				skb->dev->type = dev_type;
++				skb->mac_len = ETH_HLEN;
++#endif
++			}
++		}
++		else
++		{
++			if (!priv)
++				return -1;
++			dev = priv->dpa_bp->dev;
++			netdev = priv->net_dev;
++		}
++		/*FIXME: skb->dev->type other than ARPHRD_ETHER and ARPHRD_PPP priv is uninitialized. Here else case should add to address it. */
++		/*net_crit_ratelimited("%s(%d) x->handle %d, skb->data %p, machdr %p, len %d, skb->dev %p\n",
++			__func__,__LINE__, sagd, skb->data, skb_mac_header(skb),skb->len, skb->dev); */
++		err =  skb_fraglist_to_sg_fd(dev, netdev, skb, &fd, 0);
++		if (unlikely(err < 0))
++		{
++			printk("%s(%d) skb_fraglist_to_sg_fd failed\n",
++					__func__,__LINE__);
++			if (skb->dev->type == ARPHRD_PPP)
++				memcpy(skb->data, data, ETH_HLEN);
++
++			skb->len -= tmp_len;
++			skb->data += tmp_len;
++			return -1; /* give packet to linux */
++		}
++
++		for (ii = 0; ii < 100000; ii++) {
++			err = qman_enqueue(sec_fq, &fd, 0);
++			if (err != -EBUSY)
++				break;
++		}
++
++		if (unlikely(err < 0)) {
++			struct bm_buffer bmb;
++			dma_addr_t addr;
++
++			printk("%s(%d) qman_enqueue failed\n",
++					__func__,__LINE__);
++			/* put the buffer back to sg pool */
++			addr = qm_fd_addr(&fd);
++			bm_buffer_set64(&bmb, addr);
++			while (bman_release(sg_bpool_g->pool, &bmb, 1, 0))
++				cpu_relax();
++			return -1; /* give packet to linux */
++		}
++		if (priv)
++		{
++			percpu_priv = raw_cpu_ptr(priv->percpu_priv);
++			percpu_priv->tx_caam_dec++;
++		}
++	}
++	return 0;
++}
++#endif
++#endif
++
++#ifdef CONFIG_CPE_FAST_PATH
++#define CHANNEL_BIT_POSITION 24
++
++int cpe_fp_tx(struct sk_buff *skb, struct net_device *net_dev)
+ {
+ 	struct dpa_priv_s	*priv;
++	struct dpa_percpu_priv_s *percpu_priv;
++	int channel_id;
++	int queuenum;
++	int markval,ff=0;
++	struct qman_fq *egress_fq, *conf_fq;
++	uint8_t get_ceetm_cm_egress_fq;
++	uint8_t dscp;
++	unsigned char* skb_nh ;
++	uint16_t l3_proto = 0, l3_offset = 0;
++
++	priv = netdev_priv(net_dev);
++#ifdef CONFIG_XFRM
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	if(skb->ipsec_offload) {
++		dpaa_submit_outb_pkt_to_SEC(skb, net_dev, priv->dpa_bp);
++		percpu_priv = raw_cpu_ptr(priv->percpu_priv);
++		percpu_priv->tx_caam_enc++;
++		return NETDEV_TX_OK;
++	}
++#endif
++#endif
++	if (priv->ceetm_en) {
++		/* markval also using to know qosconnmark is present for this packet. */
++		/* If markval is 0 assuming qosconnmark is not present for this packet(connection). */
++		markval = pfe_eth_get_queuenum(skb);
++		queuenum = markval & 0xf;
++		channel_id = (markval >> CHANNEL_BIT_POSITION) & 0xf;
++		egress_fq = NULL;
++		get_ceetm_cm_egress_fq = 1; /* This becomes 0 when qosconnark(markval is 0) is not present or 
++						can't find egress fq for dscp */
++		/* markval is 0 means qosconnmark is not configured, so getting dscp from skb. */
++		/* using dscp get the egress fq. */
++		if (!markval) {
++			skb_nh = dpa_get_skb_nh(skb, &l3_proto, &l3_offset);
++			if (skb_nh)
++			{
++				/* skb_nh is non NULL means either it is pointing to ipv4 or ipv6 header only */
++				if (l3_proto ==  htons(ETH_P_IP))
++				{
++					/* Getting the DSCP value from IPv4 header*/
++					dscp = (((struct iphdr *)skb_nh)->tos) >> 2; /* tos 8 bit size, DSCP 6 bit value, shifting 2 bits right to get actual DSCP value*/
++				}
++				else
++				{
++					 /* Getting the DSCP value from IPv6 header*/
++					dscp = ((((struct ipv6hdr *)skb_nh)->priority) << 2) /* priority is 4 bit size, shifting 2 bits left to give space for flow_lbl[0] 2 bits */
++						+ ((((struct ipv6hdr *)skb_nh)->flow_lbl[0]) >> 6); /* flow_lbl[0] is 8 bit size, DSCP 2 bits are here, so shifting 6 bits right*/
++				}
++				egress_fq = ceetm_dscp_fqget_func(priv->qm_ctx, dscp);
++				if (egress_fq)
++					get_ceetm_cm_egress_fq = 0;
++			}
++		}
++		if (get_ceetm_cm_egress_fq)
++		{
++			if (ceetm_fqget_func) {
++				egress_fq = ceetm_fqget_func(priv->qm_ctx, 
++					channel_id,queuenum,ff);
++			}
++			if (!egress_fq) {
++				printk(KERN_CRIT "%s::unable to get ceetm fq, mark %08x registered func %p"
++						"dropping packet\n",
++						__func__, markval , ceetm_fqget_func);
++				dev_kfree_skb(skb);
++				return NETDEV_TX_OK;
++			}
++		}
++	}  else
++	{
++		queuenum = dpa_get_queue_mapping(skb);
++		if (unlikely(queuenum >= DPAA_ETH_TX_QUEUES))
++			queuenum = queuenum % DPAA_ETH_TX_QUEUES;
++		egress_fq = priv->egress_fqs[queuenum];
++	}
++	conf_fq = priv->conf_fqs[queuenum  & (DPAA_ETH_TX_QUEUES-1)];
++	return dpa_tx_extended(skb, net_dev, egress_fq, conf_fq);
++}
++#endif
++
++static inline void skb_reset_truesize(struct sk_buff *skb, unsigned int size)
++{
++	size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
++	skb->truesize = SKB_TRUESIZE(size);
++
++	return;
++}
++
++int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev)
++{
++	struct dpa_priv_s       *priv;
+ 	int queue_mapping = dpa_get_queue_mapping(skb);
+ 	struct qman_fq *egress_fq, *conf_fq;
+ 
+@@ -1033,6 +1935,10 @@ int __hot dpa_tx(struct sk_buff *skb, struct net_device *net_dev)
+ 		return NETDEV_TX_OK;
+ #endif
+ 
++#ifdef CONFIG_CPE_FAST_PATH
++	return cpe_fp_tx(skb, net_dev);
++#endif
++
+ 	priv = netdev_priv(net_dev);
+ 
+ #ifdef CONFIG_FSL_DPAA_CEETM
+@@ -1101,6 +2007,7 @@ int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev,
+ 	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ #endif /* CONFIG_FSL_DPAA_TS */
+ 
++
+ 	/* MAX_SKB_FRAGS is larger than our DPA_SGT_MAX_ENTRIES; make sure
+ 	 * we don't feed FMan with more fragments than it supports.
+ 	 * Btw, we're using the first sgt entry to store the linear part of
+@@ -1195,13 +2102,18 @@ int __hot dpa_tx_extended(struct sk_buff *skb, struct net_device *net_dev,
+ 		 * but we need the skb to look as if returned by build_skb().
+ 		 * We need to manually adjust the tailptr as well.
+ 		 */
++		/* skb_reset_truesize() resets the skb truesize, to avoid 
++		 * skb truesize gets increment continuously when the same 
++		 * skb used for next fragments.
++		 */
++		skb_reset_truesize(skb, SMP_CACHE_BYTES + DPA_SKB_SIZE(priv->dpa_bp->size) +
++				SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
+ 		skb->data = skb->head + offset;
+ 		skb_reset_tail_pointer(skb);
+ 
+ 		(*countptr)++;
+ 		percpu_priv->tx_returned++;
+ 	}
+-
+ 	if (unlikely(dpa_xmit(priv, percpu_stats, &fd, egress_fq, conf_fq) < 0))
+ 		goto xmit_failed;
+ 
diff --git a/patches/ask/kernel/0062-drivers__net__ppp__ppp_generic.c.patch b/patches/ask/kernel/0062-drivers__net__ppp__ppp_generic.c.patch
new file mode 100644
index 0000000..0fddbb6
--- /dev/null
+++ b/patches/ask/kernel/0062-drivers__net__ppp__ppp_generic.c.patch
@@ -0,0 +1,30 @@
+diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
+index f9f0f16..1f94967 100644
+--- a/drivers/net/ppp/ppp_generic.c
++++ b/drivers/net/ppp/ppp_generic.c
+@@ -55,6 +55,10 @@
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++#include <linux/jiffies.h>
++#endif
++
+ #define PPP_VERSION	"2.4.2"
+ 
+ /*
+@@ -3535,6 +3539,14 @@ ppp_connect_channel(struct channel *pch, int unit)
+ 
+  outl:
+ 	spin_unlock(&pch->upl);
++#if defined(CONFIG_CPE_FAST_PATH)
++	if ((ppp->dev) && (!ppp->closing)) {
++		rtnl_lock();
++		rtmsg_ifinfo(RTM_NEWLINK, ppp->dev, 0, GFP_KERNEL, 0, NULL);
++		rtnl_unlock();
++	}
++#endif
++
+  out:
+ 	mutex_unlock(&pn->all_ppp_mutex);
+ 	return ret;
diff --git a/patches/ask/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch b/patches/ask/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch
new file mode 100644
index 0000000..5e5b21c
--- /dev/null
+++ b/patches/ask/kernel/0068-drivers__staging__fsl_qbman__qman_high.c.patch
@@ -0,0 +1,371 @@
+diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c
+index 4085aa9a2dcb..96c3122e665b 100644
+--- a/drivers/staging/fsl_qbman/qman_high.c
++++ b/drivers/staging/fsl_qbman/qman_high.c
+@@ -34,6 +34,9 @@
+ 
+ #include "qman_low.h"
+ 
++#include <linux/net.h>
++#include <linux/netdevice.h>
++
+ /* Compilation constants */
+ #define DQRR_MAXFILL	15
+ #define EQCR_ITHRESH	4	/* if EQCR congests, interrupt threshold */
+@@ -69,6 +72,33 @@
+ 			spin_unlock(&__fq478->fqlock); \
+ 	} while (0)
+ 
++#if 1
++#define display_ceetm_cmd(a,b,c)
++#else
++#define display_ceetm_cmd(a, b, c) _display_ceetm_cmd((char *)__func__, a, b, c)
++static void _display_ceetm_cmd(char *func, uint32_t verb, void *buf, uint32_t size)
++{
++	uint8_t *ptr;
++	uint32_t ii,jj=0;
++	uint8_t buff[200];
++
++	ptr = buf;
++	jj = sprintf(buff, "%s::\n%02x ", func, verb);
++	for (ii = 1; ii <= size; ii++) {
++		if (ii && ((ii % 16) == 0))
++		{
++			buff[jj] = 0;
++			printk("%s\n", buff);
++			jj = 0;
++		}
++		jj += sprintf(buff+jj, "%02x ", *ptr);
++		ptr++;
++	}
++	buff[jj] = 0;
++	printk("%s\n\n", buff);
++}
++#endif
++
+ static inline void fq_set(struct qman_fq *fq, u32 mask)
+ {
+ 	set_bits(mask, &fq->flags);
+@@ -128,6 +158,10 @@ struct qman_portal {
+ 	u8 alloced;
+ 	/* power management data */
+ 	u32 save_isdr;
++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI
++	struct net_device *dummy_dev;
++	struct napi_struct napi;
++#endif
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ 	/* Keep a shadow copy of the DQRR on LE systems as the SW needs to
+ 	 * do byte swaps of DQRR read only memory.  First entry must be aligned
+@@ -473,7 +507,16 @@ static irqreturn_t portal_isr(__always_unused int irq, void *ptr)
+ 
+ 	/* DQRR-handling if it's interrupt-driven */
+ 	if (is & QM_PIRQ_DQRI) {
++#ifndef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI
+ 		__poll_portal_fast(p, CONFIG_FSL_QMAN_POLL_LIMIT);
++#else
++		/* Disable QMan IRQ and invoke NAPI */
++		qman_p_irqsource_remove(p, QM_PIRQ_DQRI);
++		if (napi_schedule_prep(&p->napi))
++		{
++			__napi_schedule(&p->napi);
++		}
++#endif
+ 		clear = QM_DQAVAIL_MASK | QM_PIRQ_DQRI;
+ 	}
+ 
+@@ -575,6 +618,27 @@ struct dev_pm_domain qman_portal_device_pm_domain = {
+ 	}
+ };
+ 
++
++
++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI
++static int qman_portal_dqrr_poll(struct napi_struct *napi, int budget)
++{
++	struct qman_portal *portal = container_of(napi, struct qman_portal, napi);
++
++	int cleaned = qman_p_poll_dqrr(portal, budget);
++
++	if (cleaned < budget) {
++		int tmp;
++		napi_complete(napi);
++		tmp = qman_p_irqsource_add(portal, QM_PIRQ_DQRI);
++		//     DPA_BUG_ON(tmp);
++	}
++
++	return cleaned;
++}
++#endif
++
++
+ struct qman_portal *qman_create_portal(
+ 			struct qman_portal *portal,
+ 			const struct qm_portal_config *config,
+@@ -737,6 +801,15 @@ struct qman_portal *qman_create_portal(
+ 			goto fail_dqrr_mr_empty;
+ 		}
+ 	}
++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI
++	/* Initialize NAPI for Rx processing */
++	portal->dummy_dev = alloc_netdev_dummy(0);
++	if (!portal->dummy_dev)
++		goto fail_dqrr_mr_empty;
++
++	netif_napi_add(portal->dummy_dev, &portal->napi, qman_portal_dqrr_poll);
++	napi_enable(&portal->napi);
++#endif
+ 	/* Success */
+ 	portal->config = config;
+ 	/*
+@@ -832,6 +902,15 @@ void qman_destroy_portal(struct qman_portal *qm)
+ 	const struct qm_portal_config *pcfg;
+ 	int i;
+ 
++#ifdef CONFIG_FSL_ASK_QMAN_PORTAL_NAPI
++	if (qm->dummy_dev) {
++		napi_disable(&qm->napi);
++		netif_napi_del(&qm->napi);
++		free_netdev(qm->dummy_dev);
++		qm->dummy_dev = NULL;
++	}
++#endif
++
+ 	/* Stop dequeues on the portal */
+ 	qm_dqrr_sdqcr_set(&qm->p, 0);
+ 
+@@ -3170,6 +3245,7 @@ static int qman_ceetm_configure_lfqmt(struct qm_mcc_ceetm_lfqmt_config *opts)
+ 	p = get_affine_portal();
+ 	PORTAL_IRQ_LOCK(p, irqflags);
+ 
++	display_ceetm_cmd(QM_CEETM_VERB_LFQMT_CONFIG, opts, sizeof(struct qm_mcc_ceetm_lfqmt_config));
+ 	mcc = qm_mc_start(&p->p);
+ 	mcc->lfqmt_config = *opts;
+ 	qm_mc_commit(&p->p, QM_CEETM_VERB_LFQMT_CONFIG);
+@@ -3233,6 +3309,7 @@ static int qman_ceetm_configure_cq(struct qm_mcc_ceetm_cq_config *opts)
+ 
+ 	mcc = qm_mc_start(&p->p);
+ 	mcc->cq_config = *opts;
++	display_ceetm_cmd(QM_CEETM_VERB_CQ_CONFIG, opts, sizeof(struct qm_mcc_ceetm_cq_config));
+ 	qm_mc_commit(&p->p, QM_CEETM_VERB_CQ_CONFIG);
+ 	while (!(mcr = qm_mc_result(&p->p)))
+ 		cpu_relax();
+@@ -3296,7 +3373,7 @@ static int qman_ceetm_configure_dct(struct qm_mcc_ceetm_dct_config *opts)
+ 
+ 	p = get_affine_portal();
+ 	PORTAL_IRQ_LOCK(p, irqflags);
+-
++	display_ceetm_cmd(QM_CEETM_VERB_DCT_CONFIG, opts, sizeof(struct qm_mcc_ceetm_dct_config));
+ 	mcc = qm_mc_start(&p->p);
+ 	mcc->dct_config = *opts;
+ 	qm_mc_commit(&p->p, QM_CEETM_VERB_DCT_CONFIG);
+@@ -3360,6 +3437,8 @@ static int qman_ceetm_configure_class_scheduler(
+ 
+ 	mcc = qm_mc_start(&p->p);
+ 	mcc->csch_config = *opts;
++	display_ceetm_cmd(QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG, opts,
++		sizeof(struct qm_mcc_ceetm_class_scheduler_config));
+ 	qm_mc_commit(&p->p, QM_CEETM_VERB_CLASS_SCHEDULER_CONFIG);
+ 	while (!(mcr = qm_mc_result(&p->p)))
+ 		cpu_relax();
+@@ -3410,7 +3489,7 @@ static int qman_ceetm_query_class_scheduler(struct qm_ceetm_channel *channel,
+ 	return 0;
+ }
+ 
+-static int qman_ceetm_configure_mapping_shaper_tcfc(
++int qman_ceetm_configure_mapping_shaper_tcfc(
+ 		struct qm_mcc_ceetm_mapping_shaper_tcfc_config *opts)
+ {
+ 	struct qm_mc_command *mcc;
+@@ -3423,6 +3502,8 @@ static int qman_ceetm_configure_mapping_shaper_tcfc(
+ 	PORTAL_IRQ_LOCK(p, irqflags);
+ 
+ 	mcc = qm_mc_start(&p->p);
++	display_ceetm_cmd(QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG, opts, 
++	       sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	mcc->mst_config = *opts;
+ 	qm_mc_commit(&p->p, QM_CEETM_VERB_MAPPING_SHAPER_TCFC_CONFIG);
+ 	while (!(mcr = qm_mc_result(&p->p)))
+@@ -3440,6 +3521,7 @@ static int qman_ceetm_configure_mapping_shaper_tcfc(
+ 	}
+ 	return 0;
+ }
++EXPORT_SYMBOL(qman_ceetm_configure_mapping_shaper_tcfc);
+ 
+ static int qman_ceetm_query_mapping_shaper_tcfc(
+ 		struct qm_mcc_ceetm_mapping_shaper_tcfc_query *opts,
+@@ -3485,7 +3567,7 @@ static int qman_ceetm_configure_ccgr(struct qm_mcc_ceetm_ccgr_config *opts)
+ 
+ 	p = get_affine_portal();
+ 	PORTAL_IRQ_LOCK(p, irqflags);
+-
++	display_ceetm_cmd(QM_CEETM_VERB_CCGR_CONFIG, opts, sizeof(struct qm_mcc_ceetm_ccgr_config));
+ 	mcc = qm_mc_start(&p->p);
+ 	mcc->ccgr_config = *opts;
+ 
+@@ -3903,6 +3985,7 @@ int qman_ceetm_lni_enable_shaper(struct qm_ceetm_lni *lni, int coupled,
+ 	lni->shaper_couple = coupled;
+ 	lni->oal = oal;
+ 
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
+ 	config_opts.dcpid = lni->dcp_idx;
+ 	config_opts.shaper_config.cpl = coupled;
+@@ -3936,7 +4019,8 @@ int qman_ceetm_lni_disable_shaper(struct qm_ceetm_lni *lni)
+ 		pr_err("The shaper has been disabled\n");
+ 		return -EINVAL;
+ 	}
+-
++	
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	config_opts.cid = cpu_to_be16(CEETM_COMMAND_LNI_SHAPER | lni->idx);
+ 	config_opts.dcpid = lni->dcp_idx;
+ 	config_opts.shaper_config.cpl = lni->shaper_couple;
+@@ -4173,6 +4257,7 @@ int qman_ceetm_lni_set_tcfcc(struct qm_ceetm_lni *lni,
+ 		return -EINVAL;
+ 	}
+ 
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	query_opts.cid = cpu_to_be16(CEETM_COMMAND_TCFC | lni->idx);
+ 	query_opts.dcpid = lni->dcp_idx;
+ 	if (qman_ceetm_query_mapping_shaper_tcfc(&query_opts, &query_result)) {
+@@ -4254,6 +4339,7 @@ int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel,
+ 	p = kzalloc(sizeof(*p), GFP_KERNEL);
+ 	if (!p)
+ 		return -ENOMEM;
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	p->idx = channel_idx;
+ 	p->dcp_idx = lni->dcp_idx;
+ 	p->lni_idx = lni->idx;
+@@ -4264,7 +4350,7 @@ int qman_ceetm_channel_claim(struct qm_ceetm_channel **channel,
+ 						channel_idx);
+ 	config_opts.dcpid = lni->dcp_idx;
+ 	config_opts.channel_mapping.map_lni_id = lni->idx;
+-	config_opts.channel_mapping.map_shaped = 0;
++	config_opts.channel_mapping.map_shaped = 1;
+ 	if (qman_ceetm_configure_mapping_shaper_tcfc(&config_opts)) {
+ 		pr_err("Can't map channel#%d for LNI#%d\n",
+ 						channel_idx, lni->idx);
+@@ -4296,7 +4382,7 @@ int qman_ceetm_channel_release(struct qm_ceetm_channel *channel)
+ 			channel->dcp_idx);
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
+ 				      channel->idx);
+ 	config_opts.dcpid = channel->dcp_idx;
+@@ -4334,7 +4420,7 @@ int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel,
+ 		pr_err("This channel shaper has been enabled!\n");
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	channel->shaper_enable = 1;
+ 	channel->shaper_couple = coupled;
+ 
+@@ -4347,6 +4433,7 @@ int qman_ceetm_channel_enable_shaper(struct qm_ceetm_channel *channel,
+ 		return -EINVAL;
+ 	}
+ 
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_MAPPING |
+ 						channel->idx);
+ 	config_opts.dcpid = channel->dcp_idx;
+@@ -4441,7 +4528,7 @@ int qman_ceetm_channel_set_commit_rate(struct qm_ceetm_channel *channel,
+ 		pr_err("Fail to get the current channel shaper setting\n");
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	channel->cr_token_rate.whole = token_rate->whole;
+ 	channel->cr_token_rate.fraction = token_rate->fraction;
+ 	channel->cr_token_bucket_limit = token_limit;
+@@ -4534,7 +4621,8 @@ int qman_ceetm_channel_set_excess_rate(struct qm_ceetm_channel *channel,
+ 		pr_err("Fail to get the current channel shaper setting\n");
+ 		return -EINVAL;
+ 	}
+-
++	
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	channel->er_token_rate.whole = token_rate->whole;
+ 	channel->er_token_rate.fraction = token_rate->fraction;
+ 	channel->er_token_bucket_limit = token_limit;
+@@ -4618,7 +4706,7 @@ int qman_ceetm_channel_set_weight(struct qm_ceetm_channel *channel,
+ 		pr_err("This channel is a shaped one\n");
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	channel->cr_token_bucket_limit = token_limit;
+ 	config_opts.cid = cpu_to_be16(CEETM_COMMAND_CHANNEL_SHAPER |
+ 						channel->idx);
+@@ -4668,7 +4756,7 @@ int qman_ceetm_channel_set_group(struct qm_ceetm_channel *channel, int group_b,
+ 		pr_err("Can't query channel#%d's scheduler!\n", channel->idx);
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_mapping_shaper_tcfc_config));
+ 	config_opts.cqcid = cpu_to_be16(channel->idx);
+ 	config_opts.dcpid = channel->dcp_idx;
+ 	config_opts.gpc_combine_flag = !group_b;
+@@ -4759,6 +4847,7 @@ int qman_ceetm_channel_set_group_er_eligibility(struct qm_ceetm_channel
+ 						channel->idx);
+ 		return -EINVAL;
+ 	}
++	memset(&csch_config, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config));
+ 	csch_config.cqcid = cpu_to_be16(channel->idx);
+ 	csch_config.dcpid = channel->dcp_idx;
+ 	csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
+@@ -4806,6 +4895,7 @@ int qman_ceetm_channel_set_cq_cr_eligibility(struct qm_ceetm_channel *channel,
+ 						channel->idx);
+ 		return -EINVAL;
+ 	}
++	memset(&csch_config, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config));
+ 	csch_config.cqcid = cpu_to_be16(channel->idx);
+ 	csch_config.dcpid = channel->dcp_idx;
+ 	csch_config.gpc_combine_flag = csch_query.gpc_combine_flag;
+@@ -4889,7 +4979,7 @@ int qman_ceetm_cq_claim(struct qm_ceetm_cq **cq,
+ 		pr_err("Can't allocate memory for CQ#%d!\n", idx);
+ 		return -ENOMEM;
+ 	}
+-
++	memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config));
+ 	list_add_tail(&p->node, &channel->class_queues);
+ 	p->idx = idx;
+ 	p->is_claimed = 1;
+@@ -4938,7 +5028,7 @@ int qman_ceetm_cq_claim_A(struct qm_ceetm_cq **cq,
+ 		pr_err("Can't allocate memory for CQ#%d!\n", idx);
+ 		return -ENOMEM;
+ 	}
+-
++	memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config));
+ 	list_add_tail(&p->node, &channel->class_queues);
+ 	p->idx = idx;
+ 	p->is_claimed = 1;
+@@ -4986,7 +5076,7 @@ int qman_ceetm_cq_claim_B(struct qm_ceetm_cq **cq,
+ 		pr_err("Can't allocate memory for CQ#%d!\n", idx);
+ 		return -ENOMEM;
+ 	}
+-
++	memset(&cq_config, 0, sizeof(struct qm_mcc_ceetm_cq_config));
+ 	list_add_tail(&p->node, &channel->class_queues);
+ 	p->idx = idx;
+ 	p->is_claimed = 1;
+@@ -5040,7 +5130,7 @@ int qman_ceetm_set_queue_weight(struct qm_ceetm_cq *cq,
+ 						cq->parent->idx);
+ 		return -EINVAL;
+ 	}
+-
++	memset(&config_opts, 0, sizeof(struct qm_mcc_ceetm_class_scheduler_config));
+ 	config_opts.cqcid = cpu_to_be16(cq->parent->idx);
+ 	config_opts.dcpid = cq->parent->dcp_idx;
+ 	config_opts.crem = query_result.crem;
+@@ -5257,6 +5347,7 @@ int qman_ceetm_lfq_claim(struct qm_ceetm_lfq **lfq,
+ 	p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 	if (!p)
+ 		return -ENOMEM;
++	memset(&lfqmt_config, 0, sizeof(struct qm_mcc_ceetm_lfqmt_config));
+ 	p->idx = lfqid;
+ 	p->dctidx = (u16)(lfqid & CEETM_LFQMT_LFQID_LSB);
+ 	p->parent = cq->parent;
diff --git a/patches/ask/kernel/0071-include__linux__if_bridge.h.patch b/patches/ask/kernel/0071-include__linux__if_bridge.h.patch
new file mode 100644
index 0000000..6bf854c
--- /dev/null
+++ b/patches/ask/kernel/0071-include__linux__if_bridge.h.patch
@@ -0,0 +1,28 @@
+diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
+index 6d4e3d1b1111..7c0d4c2e2222 100644
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -65,6 +65,23 @@ void brioctl_set(int (*hook)(struct net *net, unsigned int cmd,
+ 			     void __user *uarg));
+ int br_ioctl_call(struct net *net, unsigned int cmd, void __user *uarg);
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++struct brevent_fdb_update {
++	char *mac_addr;
++	struct net_device *dev;
++	struct net_device *brdev;
++};
++
++enum brevent_notif_type {
++	BREVENT_PORT_DOWN = 1,  /* arg is struct net_device ptr */
++	BREVENT_FDB_UPDATE  /* arg is struct brevent_fdb_update ptr */
++};
++
++int register_brevent_notifier(struct notifier_block *nb);
++int unregister_brevent_notifier(struct notifier_block *nb);
++int call_brevent_notifiers(unsigned long val, void *v);
++#endif
++
+ #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
+ int br_multicast_list_adjacent(struct net_device *dev,
+ 			       struct list_head *br_ip_list);
diff --git a/patches/ask/kernel/0078-include__net__xfrm.h.patch b/patches/ask/kernel/0078-include__net__xfrm.h.patch
new file mode 100644
index 0000000..5e11b93
--- /dev/null
+++ b/patches/ask/kernel/0078-include__net__xfrm.h.patch
@@ -0,0 +1,76 @@
+diff --git a/include/net/xfrm.h b/include/net/xfrm.h
+index 0a14daa..ff8a1ad 100644
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -196,6 +196,12 @@ struct xfrm_state {
+ 		struct hlist_node	bysrc;
+ 	};
+ 	struct hlist_node	byspi;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	struct hlist_node	byh;
++	u16			handle;
++	u16			in_byh_hash;
++	u16			parent_sa_handle; /* handle of the old SA from which this SA is created using rekey */
++#endif
+ 	struct hlist_node	byseq;
+ 	struct hlist_node	state_cache;
+ 	struct hlist_node	state_cache_input;
+@@ -314,6 +320,11 @@ struct xfrm_state {
+ 	/* Private data of this transformer, format is opaque,
+ 	 * interpreted by xfrm_type methods. */
+ 	void			*data;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	/* Intended direction of this state, used for offloading */
++	int			offloaded;
++	u64			curr_time;
++#endif
+ 	u8			dir;
+ 
+ 	const struct xfrm_mode_cbs	*mode_cbs;
+@@ -337,6 +348,13 @@ enum {
+ 	XFRM_STATE_EXPIRED,
+ 	XFRM_STATE_DEAD
+ };
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++enum {
++	XFRM_STATE_DIR_UNKNOWN,
++	XFRM_STATE_DIR_IN,
++	XFRM_STATE_DIR_OUT,
++};
++#endif
+ 
+ /* callback structure passed from either netlink or pfkey */
+ struct km_event {
+@@ -1173,6 +1191,32 @@ struct sec_path {
+ 
+ struct sec_path *secpath_set(struct sk_buff *skb);
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++struct xfrm_input_shared {
++	struct sk_buff		*skb;
++	int			xfrm_nr, first, xfrm_encap;
++	struct xfrm_state	*xfrm_vec[XFRM_MAX_DEPTH];
++	__u16			encap_type;
++	int			decaps;
++	u32			seq, spi;
++	unsigned int		nhoff;
++	int			nexthdr;
++	int			(*callback)(struct xfrm_input_shared *sh);
++	atomic_t		refcnt;
++};
++
++static inline void xfrm_shared_get(struct xfrm_input_shared *sh)
++{
++	atomic_inc(&sh->refcnt);
++}
++
++static inline void xfrm_shared_put(struct xfrm_input_shared *sh)
++{
++	if (atomic_dec_and_test(&sh->refcnt))
++		kfree(sh);
++}
++#endif
++
+ static inline void
+ secpath_reset(struct sk_buff *skb)
+ {
diff --git a/patches/ask/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch b/patches/ask/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch
new file mode 100644
index 0000000..8ba47df
--- /dev/null
+++ b/patches/ask/kernel/0086-include__uapi__linux__netfilter__nfnetlink_conntrack.h.patch
@@ -0,0 +1,36 @@
+diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+index 43233af..2e401ae 100644
+--- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h
++++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+@@ -58,6 +58,10 @@ enum ctattr_type {
+ 	CTA_FILTER,
+ 	CTA_STATUS_MASK,
+ 	CTA_TIMESTAMP_EVENT,
++	CTA_LAYERSCAPE_FP_ORIG,
++	CTA_LAYERSCAPE_FP_REPLY,
++	CTA_QOSCONNMARK,
++	CTA_QOSCONNMARK_PAD,
+ 	__CTA_MAX
+ };
+ #define CTA_MAX (__CTA_MAX - 1)
+@@ -243,6 +247,20 @@ enum ctattr_secctx {
+ };
+ #define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1)
+ 
++enum ctattr_comcerto_fp {
++	CTA_COMCERTO_FP_UNSPEC,
++	CTA_COMCERTO_FP_MARK,
++	CTA_COMCERTO_FP_IFINDEX,
++	CTA_COMCERTO_FP_IIF,
++	CTA_COMCERTO_FP_UNDERLYING_IIF,
++	CTA_COMCERTO_FP_UNDERLYING_VID,
++#ifndef IPSEC_FLOW_CACHE
++	CTA_COMCERTO_FP_XFRM_HANDLE,
++#endif
++	__CTA_COMCERTO_FP_MAX
++};
++#define CTA_COMCERTO_FP_MAX (__CTA_COMCERTO_FP_MAX - 1)
++
+ enum ctattr_stats_cpu {
+ 	CTA_STATS_UNSPEC,
+ 	CTA_STATS_SEARCHED,	/* no longer used */
diff --git a/patches/ask/kernel/0100-net__bridge__br_private.h.patch b/patches/ask/kernel/0100-net__bridge__br_private.h.patch
new file mode 100644
index 0000000..189ee5b
--- /dev/null
+++ b/patches/ask/kernel/0100-net__bridge__br_private.h.patch
@@ -0,0 +1,28 @@
+diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
+index 9a3d6f2b8c1e..b4f7b2c1d9aa 100644
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -620,6 +620,11 @@ struct br_input_skb_cb {
+ #endif
+ 
+ 	u32 backup_nhid;
++
++#ifdef CONFIG_CPE_FAST_PATH
++	u16 vid;
++	u8 untagged:1;
++#endif
+ };
+ 
+ #define BR_INPUT_SKB_CB(__skb)	((struct br_input_skb_cb *)(__skb)->cb)
+@@ -859,6 +864,11 @@ int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
+ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
+ 		   const unsigned char *addr, u16 vid, unsigned long flags);
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++extern void br_fdb_register_can_expire_cb(int(*cb)(unsigned char *mac_addr, struct net_device *dev));
++extern void br_fdb_deregister_can_expire_cb(void);
++#endif
++
+ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
+ 		  struct net_device *dev, const unsigned char *addr, u16 vid,
+ 		  bool *notified, struct netlink_ext_ack *extack);
diff --git a/patches/ask/kernel/0103-net__core__dev.c.patch b/patches/ask/kernel/0103-net__core__dev.c.patch
new file mode 100644
index 0000000..78bdc3e
--- /dev/null
+++ b/patches/ask/kernel/0103-net__core__dev.c.patch
@@ -0,0 +1,131 @@
+diff --git a/net/core/dev.c b/net/core/dev.c
+index 2acfa44..02e304b 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -176,6 +176,10 @@ static int call_netdevice_notifiers_extack(unsigned long val,
+ 					   struct net_device *dev,
+ 					   struct netlink_ext_ack *extack);
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++static fp_iface_stats_get fast_path_stats_get;
++#endif
++
+ static DEFINE_MUTEX(ifalias_mutex);
+ 
+ /* protects napi_hash addition/deletion and napi_gen_id */
+@@ -4002,9 +4006,15 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
+ 			skb = segs;
+ 		}
+ 	} else {
+-		if (skb_needs_linearize(skb, features) &&
+-		    __skb_linearize(skb))
+-			goto out_kfree_skb;
++		/* Linearize only if IPsec policy is not selected. */
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		if (!skb->ipsec_offload)
++#endif
++		{
++			if (skb_needs_linearize(skb, features) &&
++			    __skb_linearize(skb))
++				goto out_kfree_skb;
++		}
+ 
+ 		/* If packet is not checksummed and device does not
+ 		 * support checksumming for this protocol, complete
+@@ -4792,6 +4802,40 @@ out:
+ }
+ EXPORT_SYMBOL(__dev_queue_xmit);
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++/* WiFi IPsec offload hook - allows cdx to intercept packets for IPsec
++ * processing when the packet is transmitted on a wifi interface.
++ */
++dpaa_wifi_xmit_local_hook_t dpaa_wifi_xmit_local_ipsec_handler;
++EXPORT_SYMBOL(dpaa_wifi_xmit_local_ipsec_handler);
++
++/* Register a hook function for IPsec offload on wifi interfaces. */
++int dpa_register_wifi_xmit_local_hook(dpaa_wifi_xmit_local_hook_t hookfn)
++{
++	if (dpaa_wifi_xmit_local_ipsec_handler) {
++		pr_warn("%s: hook already registered\n", __func__);
++		return -1;
++	}
++	dpaa_wifi_xmit_local_ipsec_handler = hookfn;
++	return 0;
++}
++EXPORT_SYMBOL(dpa_register_wifi_xmit_local_hook);
++
++/* Unregister the IPsec offload hook. */
++void dpa_unregister_wifi_xmit_local_hook(void)
++{
++	dpaa_wifi_xmit_local_ipsec_handler = NULL;
++}
++EXPORT_SYMBOL(dpa_unregister_wifi_xmit_local_hook);
++
++/* Original dev_queue_xmit - called when wifi hook is not applicable. */
++int original_dev_queue_xmit(struct sk_buff *skb)
++{
++	return __dev_queue_xmit(skb, NULL);
++}
++EXPORT_SYMBOL(original_dev_queue_xmit);
++#endif
++
+ int __dev_direct_xmit(struct sk_buff *skb, u16 queue_id)
+ {
+ 	struct net_device *dev = skb->dev;
+@@ -5862,6 +5906,15 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
+ 
+ 	trace_netif_receive_skb(skb);
+ 
++#ifdef CONFIG_CPE_FAST_PATH
++	/* ifindex of device we arrived on, now skb->skb_iif
++	 * always tracks skb->dev.
++	 */
++	if (!skb->iif_index)
++		skb->iif_index = skb->dev->ifindex;
++	if (!skb->underlying_iif)
++		skb->underlying_iif = skb->dev->ifindex;
++#endif
+ 	orig_dev = skb->dev;
+ 
+ 	skb_reset_network_header(skb);
+@@ -7627,9 +7680,9 @@ static int __napi_poll(struct napi_struct *n, bool *repoll)
+ 		return work;
+ 	}
+ 
+ 	/* Flush too old packets. If HZ < 1000, flush all packets */
+-	gro_flush_normal(&n->gro, HZ >= 1000);
++	gro_flush(&n->gro, HZ >= 1000);
+ 
+ 	/* Some drivers may have called napi_schedule
+ 	 * prior to exhausting their budget.
+ 	 */
+@@ -11762,10 +11812,28 @@ struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
+ 			storage->rx_otherhost_dropped += READ_ONCE(core_stats->rx_otherhost_dropped);
+ 		}
+ 	}
++#if defined(CONFIG_CPE_FAST_PATH)
++	if (fast_path_stats_get)
++		fast_path_stats_get(dev, storage);
++#endif
+ 	return storage;
+ }
+ EXPORT_SYMBOL(dev_get_stats);
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++void dev_fp_stats_get_register(fp_iface_stats_get func)
++{
++	fast_path_stats_get = func;
++}
++EXPORT_SYMBOL(dev_fp_stats_get_register);
++
++void dev_fp_stats_get_deregister(void)
++{
++	fast_path_stats_get = NULL;
++}
++EXPORT_SYMBOL(dev_fp_stats_get_deregister);
++#endif
++
+ /**
+  *	dev_fetch_sw_netstats - get per-cpu network device statistics
+  *	@s: place to store stats
diff --git a/patches/ask/kernel/0108-net__ipv4__ip_output.c.patch b/patches/ask/kernel/0108-net__ipv4__ip_output.c.patch
new file mode 100644
index 0000000..6a16fb0
--- /dev/null
+++ b/patches/ask/kernel/0108-net__ipv4__ip_output.c.patch
@@ -0,0 +1,79 @@
+diff -uNr a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
+--- a/net/ipv4/ip_output.c	2026-05-08 18:06:42.017639432 +0000
++++ b/net/ipv4/ip_output.c	2026-05-08 18:06:42.100831810 +0000
+@@ -103,6 +103,17 @@
+ {
+ 	struct iphdr *iph = ip_hdr(skb);
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)
++	/*
++	 * The tunnel header is not added in slow path and packet may be ipv6
++	 * (IPv6 traffic and IPv4 IPSec tunnel) in case IPv6 over IPv4 IPsec
++	 * tunnel. When it assumes as IPv4 and accessing IP header from SKB
++	 * causing invalid accesses it leading to kernel panic.
++	 * Avoiding ip header checks.
++	 */
++	if ((skb->ipsec_offload) && (iph->version == 6))
++		goto sendout;
++#endif /* endif for CONFIG_INET_IPSEC_OFFLOAD */
+ 	IP_INC_STATS(net, IPSTATS_MIB_OUTREQUESTS);
+ 
+ 	iph_set_totlen(iph, skb->len);
+@@ -115,8 +126,17 @@
+ 	if (unlikely(!skb))
+ 		return 0;
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)
++sendout:
++#endif
+ 	skb->protocol = htons(ETH_P_IP);
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)
++	if (skb->ipsec_offload) {
++		dst_output(net, sk, skb);
++		return 0;
++	} else
++#endif /* endif for CONFIG_INET_IPSEC_OFFLOAD */
+ 	return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
+ 		       net, sk, skb, NULL, skb_dst_dev(skb),
+ 		       dst_output);
+@@ -309,7 +329,11 @@
+ 	if (skb_is_gso(skb))
+ 		return ip_finish_output_gso(net, sk, skb, mtu);
+ 
+-	if (skb->len > mtu || IPCB(skb)->frag_max_size)
++	if (
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)
++		(skb->ipsec_offload == 0) &&
++#endif
++		(skb->len > mtu || IPCB(skb)->frag_max_size))
+ 		return ip_fragment(net, sk, skb, mtu, ip_finish_output2);
+ 
+ 	return ip_finish_output2(net, sk, skb);
+@@ -435,6 +459,16 @@
+ 	skb->dev = dev;
+ 	skb->protocol = htons(ETH_P_IP);
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)
++	/* Bypass invoking post routing hooks since the tunnel header and ESP
++	 * processing is not done in slow path for IPSec offloaded cases
++	 */
++	if (skb->ipsec_offload) {
++		ret_val = ip_finish_output(net, sk, skb);
++		rcu_read_unlock();
++		return ret_val;
++	}
++#endif
+ 	ret_val = NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+ 				net, sk, skb, indev, dev,
+ 				ip_finish_output,
+@@ -559,6 +593,9 @@
+ 	skb_dst_copy(to, from);
+ 	to->dev = from->dev;
+ 	to->mark = from->mark;
++#if defined(CONFIG_CPE_FAST_PATH)
++	to->qosmark = from->qosmark;
++#endif
+ 
+ 	skb_copy_hash(to, from);
+ 
diff --git a/patches/ask/kernel/0112-net__ipv6__ip6_output.c.patch b/patches/ask/kernel/0112-net__ipv6__ip6_output.c.patch
new file mode 100644
index 0000000..3158c5f
--- /dev/null
+++ b/patches/ask/kernel/0112-net__ipv6__ip6_output.c.patch
@@ -0,0 +1,73 @@
+diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
+index f904739..e26e743 100644
+--- a/net/ipv6/ip6_output.c
++++ b/net/ipv6/ip6_output.c
+@@ -80,6 +80,13 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+ 
+ 	hdr = ipv6_hdr(skb);
+ 	daddr = &hdr->daddr;
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	/* For IPv4 over IPv6 IPsec tunnel cases, just send the packet out
++	 * since the packet is IPv4
++	 */
++	if((skb->ipsec_offload) && (hdr->version == 4))
++		goto sendout;
++#endif /* endif for CONFIG_INET6_IPSEC_OFFLOAD */
+ 	if (ipv6_addr_is_multicast(daddr)) {
+ 		if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
+ 		    ((mroute6_is_socket(net, skb) &&
+@@ -111,6 +118,9 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
+ 		}
+ 	}
+ 
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++sendout:
++#endif /* endif for CONFIG_INET6_IPSEC_OFFLOAD */
+ 	if (lwtunnel_xmit_redirect(dst->lwtstate)) {
+ 		int res = lwtunnel_xmit(skb);
+ 
+@@ -202,8 +212,15 @@ static int __ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff
+ 	if (skb_is_gso(skb))
+ 		return ip6_finish_output_gso(net, sk, skb, mtu);
+ 
+-	if (skb->len > mtu ||
+-	    (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
++	if (
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		/* If ipsec offload is there, do not do fragment. So, when IPSec
++		 * offload is enabled it directly calls ip6_finish_output2
++		 */
++		(skb->ipsec_offload == 0) &&
++#endif
++		((skb->len > mtu) ||
++		(IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)))
+ 		return ip6_fragment(net, sk, skb, ip6_finish_output2);
+ 
+ 	return ip6_finish_output2(net, sk, skb);
+@@ -244,6 +261,16 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+ 		return 0;
+ 	}
+ 
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	/* Bypass invoking post routing hooks since the tunnel header and ESP
++	 * processing is not done in slow path for IPSec offloaded cases
++	 */
++	if (skb->ipsec_offload) {
++		ret = ip6_finish_output(net, sk, skb);
++		rcu_read_unlock();
++		return ret;
++	}
++#endif
+ 	ret = NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
+ 			   net, sk, skb, indev, dev,
+ 			   ip6_finish_output,
+@@ -697,6 +724,9 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
+ 	skb_dst_set(to, dst_clone(skb_dst(from)));
+ 	to->dev = from->dev;
+ 	to->mark = from->mark;
++#if defined(CONFIG_CPE_FAST_PATH)
++	to->qosmark = from->qosmark;
++#endif
+ 
+ 	skb_copy_hash(to, from);
+ 
diff --git a/patches/ask/kernel/0115-net__ipv6__output_core.c.patch b/patches/ask/kernel/0115-net__ipv6__output_core.c.patch
new file mode 100644
index 0000000..65c7704
--- /dev/null
+++ b/patches/ask/kernel/0115-net__ipv6__output_core.c.patch
@@ -0,0 +1,38 @@
+diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
+index 1c9b283..5682505 100644
+--- a/net/ipv6/output_core.c
++++ b/net/ipv6/output_core.c
+@@ -130,7 +130,19 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
+ 	len = skb->len - sizeof(struct ipv6hdr);
+ 	if (len > IPV6_MAXPLEN)
+ 		len = 0;
++
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	/*
++	 * Since tunnel header is not added in slow path and route cache entry
++	 * lookup is changed to perform lookup on tunnel header, dst_output()
++	 * may reach IPv6 output for IPv4-over-IPv6 IPsec tunnel packets. Only
++	 * update the IPv6 payload length when the skb really carries IPv6 here.
++	 */
++	if (ipv6_hdr(skb)->version == 6)
++		ipv6_hdr(skb)->payload_len = htons(len);
++#else
+ 	ipv6_hdr(skb)->payload_len = htons(len);
++#endif
+ 	IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+ 
+ 	/* if egress device is enslaved to an L3 master device pass the
+@@ -142,6 +154,13 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
+ 
+ 	skb->protocol = htons(ETH_P_IPV6);
+ 
++#if defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	if (skb->ipsec_offload) {
++		dst_output(net, sk, skb);
++		return 0;
++	}
++#endif
++
+ 	return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+ 		       net, sk, skb, NULL, skb_dst_dev(skb),
+ 		       dst_output);
diff --git a/patches/ask/kernel/0119-net__key__af_key.c.patch b/patches/ask/kernel/0119-net__key__af_key.c.patch
new file mode 100644
index 0000000..39a33ab
--- /dev/null
+++ b/patches/ask/kernel/0119-net__key__af_key.c.patch
@@ -0,0 +1,949 @@
+diff --git a/net/key/af_key.c b/net/key/af_key.c
+index c56bb4f451e6..7225be6880e1 100644
+--- a/net/key/af_key.c
++++ b/net/key/af_key.c
+@@ -26,8 +26,184 @@
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+ #include <net/xfrm.h>
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD)
++#include <net/netlink.h>
++#endif
+ 
+ #include <net/sock.h>
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD)
++#include <net/ip6_route.h>
++#define NLKEY_SUPPORT 1
++#else 
++#undef NLKEY_SUPPORT
++#endif 
++
++#ifdef NLKEY_SUPPORT
++#include <net/dsfield.h>
++#include <net/inet_dscp.h>
++#include <net/inet_ecn.h>
++#include <net/ipv6.h>
++
++
++extern int xfrm_get_tos(struct flowi *fl, int family);
++
++
++#define	NLKEY_SA_CREATE		0x0A01
++#define NLKEY_SA_DELETE		0x0A02
++#define NLKEY_SA_FLUSH 		0x0A03
++#define NLKEY_SA_SET_KEYS	0x0A04
++#define NLKEY_SA_SET_TUNNEL	0x0A05
++#define NLKEY_SA_SET_NATT	0x0A06
++#define	NLKEY_SA_SET_STATE	0x0A07
++#define	NLKEY_SA_SET_LIFETIME	0x0A08
++#define	NLKEY_SA_NOTIFY		0x0A09
++#define NLKEY_SA_INFO_UPDATE	0x0A0C
++#define NLKEY_SA_SET_OFFLOAD	0x0A0D
++#define	NLKEY_FLOW_ADD		0x0A11
++#define NLKEY_FLOW_REMOVE	0x0A12
++#define NLKEY_FLOW_NOTIFY	0x0A13
++#define NLKEY_NULL_MSG		0x0000
++
++#define NLKEY_HDR_LEN		4
++#define NLKEY_MSG_LEN 		256
++
++#define NLKEY_MAX_NUM_KEYS	2
++#define NLKEY_MAX_KEY_LEN	(512 / 8)
++
++struct nlkey_msg {
++	/* message data */
++	unsigned short fcode;
++	unsigned short length;
++	unsigned short payload[(NLKEY_MSG_LEN /sizeof(unsigned short))];
++};
++/* sizeof(nlkey_msg) = 4 + 256 */
++
++struct nlkey_sa_id {
++	unsigned int spi;
++	unsigned char sa_type;
++	unsigned char proto_family;
++	unsigned char replay_window;
++#define NLKEY_SAFLAGS_ESN	0x1
++#define NLKEY_SAFLAGS_INBOUND	0x2
++	unsigned char flags;
++	unsigned int dst_ip[4];
++	unsigned int src_ip[4];
++	unsigned short mtu;
++	unsigned short dev_mtu;
++
++};
++/* sizeof(nlkey_sa_id) = 24 */
++
++struct nlkey_sa_create {
++	unsigned short sagd;
++	unsigned short parent_sa_sagd; /*sagd value of old SA from which this SA is rekeyed.*/
++	struct nlkey_sa_id said;
++};
++/* sizeof(nlkey_sa_delete) = 28 */
++
++struct nlkey_sa_delete {
++	unsigned short sagd;
++	unsigned short rsvd;
++};
++/* sizeof(nlkey_sa_delete) = 4 */
++
++struct nlkey_sa_set_tunnel {
++	unsigned short sagd;
++	unsigned char rsvd;
++	unsigned char proto_family;
++	union {
++		struct iphdr 	 ipv4h;
++		struct ipv6hdr ipv6h;
++	} h;
++};
++/* sizeof(nlkey_sa_set_tunnel) = 36 */
++
++struct nlkey_sa_set_natt {
++	unsigned short sagd;
++	unsigned short sport;
++	unsigned short dport;
++	unsigned short rsvd;
++};
++/* sizeof(nlkey_sa_set_natt) = 4 */
++
++struct nlkey_sa_set_state {
++	unsigned short sagd;
++	unsigned short parent_sa_sagd;
++	unsigned short state;
++	unsigned short rsvd2;
++};
++/* sizeof(nlkey_sa_set_natt) = 8 */
++
++struct nlkey_key_desc {
++	unsigned short key_bits;
++	unsigned char key_alg;
++	unsigned char  key_type;
++	unsigned char key[NLKEY_MAX_KEY_LEN]; 
++};
++/* sizeof(nlkey_key_desc) =  36 */
++
++struct nlkey_sa_set_keys {
++	unsigned short sagd;
++	unsigned short rsvd;	
++	unsigned short num_keys;
++	unsigned short rsvd2;
++	struct nlkey_key_desc keys[NLKEY_MAX_NUM_KEYS];
++};
++/* sizeof(nlkey_sa_set_keys) =  80 */
++
++struct nlkey_lifetime_desc {
++	unsigned int allocations;
++	unsigned int bytes[2];
++};
++/* sizeof(nlkey_sa_set_lifetime) =  12 */
++
++struct nlkey_sa_set_lifetime {
++	unsigned short sagd;
++	unsigned short rsvd;
++	struct nlkey_lifetime_desc hard_time;
++	struct nlkey_lifetime_desc soft_time;
++	struct nlkey_lifetime_desc current_time;
++};
++/* sizeof(nlkey_sa_set_lifetime) =  40 */
++
++/* SA notifications */
++#define IPSEC_SOFT_EXPIRE 0
++#define IPSEC_HARD_EXPIRE 1
++
++struct nlkey_sa_notify {
++	unsigned short sagd;
++	unsigned short rsvd;
++	unsigned int  action;
++};
++/* sizeof(nlkey_sa_notify) = 8 */
++
++/* SA Info update */
++
++struct nlkey_sa_info {
++        unsigned short sagd;
++        unsigned short rsvd;
++        unsigned long long bytes;
++        unsigned long long packets;
++};
++/* sizeof(nlkey_sa_info) =  */
++
++
++static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c);
++static void ipsec_nlkey_rcv(struct sk_buff *skb);
++static void ipsec_nlkey_init(void);
++static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code);
++static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, 
++					const struct km_event *c, unsigned short *msg_id);
++static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, const struct km_event *c, struct nlkey_sa_id *said);
++
++void flow_cache_remove(const struct flowi *fl, unsigned short family,
++		unsigned short dir);
++/* netlink NETLINK_KEY socket */
++struct sock *nlkey_socket = NULL;
++
++#endif
++/************************************************************************************/
++
+ 
+ #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))
+ #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))
+@@ -876,6 +1051,10 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
+ 		sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP;
+ 	if (x->props.flags & XFRM_STATE_NOPMTUDISC)
+ 		sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC;
++#ifdef NLKEY_SUPPORT
++	if (x->props.flags & XFRM_STATE_ESN)
++		sa->sadb_sa_flags |= SADB_SAFLAGS_ESN;
++#endif
+ 
+ 	/* hard time */
+ 	if (hsc & 2) {
+@@ -908,6 +1087,11 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x,
+ 	lifetime->sadb_lifetime_bytes = x->curlft.bytes;
+ 	lifetime->sadb_lifetime_addtime = x->curlft.add_time;
+ 	lifetime->sadb_lifetime_usetime = x->curlft.use_time;
++
++#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	lifetime->sadb_lifetime_usetime = x->curr_time;
++#endif
++
+ 	/* src address */
+ 	addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size);
+ 	addr->sadb_address_len =
+@@ -1133,6 +1317,10 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
+ 		x->props.flags |= XFRM_STATE_DECAP_DSCP;
+ 	if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC)
+ 		x->props.flags |= XFRM_STATE_NOPMTUDISC;
++#ifdef NLKEY_SUPPORT
++	if (sa->sadb_sa_flags & SADB_SAFLAGS_ESN)
++		x->props.flags |= XFRM_STATE_ESN;
++#endif
+ 
+ 	lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1];
+ 	if (lifetime != NULL) {
+@@ -3076,6 +3264,12 @@ static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
+ 	struct net *net = x ? xs_net(x) : c->net;
+ 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+ 
++
++#ifdef NLKEY_SUPPORT
++	/* send message to the user space through NETLINK_KEY socket*/
++	ipsec_nlkey_send(net, x, c);
++#endif
++
+ 	if (atomic_read(&net_pfkey->socks_nr) == 0)
+ 		return 0;
+ 
+@@ -3863,6 +4057,687 @@ static struct xfrm_mgr pfkeyv2_mgr =
+ 	.is_alive	= pfkey_is_alive,
+ };
+ 
++
++#ifdef NLKEY_SUPPORT
++extern struct xfrm_state *xfrm_state_lookup_byhandle(struct net *net, u16 handle);
++
++static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code)
++{
++	unsigned nlkey_code;
++
++	switch (sa_code) 
++	{
++		case XFRM_MSG_DELSA:
++			nlkey_code = NLKEY_SA_DELETE;
++			break;
++		case XFRM_MSG_NEWSA:
++		case XFRM_MSG_UPDSA:
++			nlkey_code = NLKEY_SA_CREATE;
++			break;
++		case XFRM_MSG_FLUSHSA:
++			nlkey_code = NLKEY_SA_FLUSH;
++			break;
++		case XFRM_MSG_EXPIRE:
++			nlkey_code = NLKEY_SA_SET_STATE;
++			break;
++		default:
++			nlkey_code = NLKEY_NULL_MSG;
++			break;
++	}
++
++	return nlkey_code;
++}
++
++static void ipsec_nlkey_rcv(struct sk_buff *skb)
++{
++	struct nlmsghdr *nlh = NULL;
++	struct nlkey_msg *msg = NULL;
++	struct flowi flow;
++	unsigned short *p;
++	unsigned short family, dir;
++	struct xfrm_state *x;
++	struct nlkey_sa_notify sa_notify_msg;
++	struct nlkey_sa_info sa_info_msg;
++
++	/* extract message from skb */
++	nlh = (struct nlmsghdr *)skb->data;
++
++	msg = (struct nlkey_msg *)NLMSG_DATA(nlh);
++
++	//printk(KERN_INFO "ipsec_nlkey_rcv fcode: 0x%x length: %d bytes\n",msg->fcode,msg->length);
++
++	/* process command received from user space */
++	switch(msg->fcode)
++	{
++		case NLKEY_FLOW_REMOVE:
++			//printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_FLOW_REMOVE\n");
++			p = msg->payload;
++			memcpy(&flow, p, sizeof(struct flowi)); p += sizeof(struct flowi)/2;
++			family = *p; p++;
++			dir = *p; p++;
++			flow_cache_remove(&flow, family, dir);
++			break;
++
++		case NLKEY_SA_NOTIFY:
++			//printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_SA_NOTIFY\n");
++			memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify));
++			x = xfrm_state_lookup_byhandle(&init_net, sa_notify_msg.sagd);
++			if (x) {
++				spin_lock(&x->lock);
++
++				if (sa_notify_msg.action) { 
++					// hard expired
++					x->km.state = XFRM_STATE_EXPIRED;
++					hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL_SOFT);
++				}
++				else if (!x->km.dying) {
++					 x->km.dying = 1;
++					 km_state_expired(x, 0, 0);
++				}
++
++				spin_unlock(&x->lock);
++				xfrm_state_put(x);
++			}
++			break;
++
++		case NLKEY_SA_INFO_UPDATE:
++			memcpy(&sa_info_msg, msg->payload, sizeof(struct nlkey_sa_info));
++
++			x = xfrm_state_lookup_byhandle(&init_net,sa_info_msg.sagd);
++			if (x) {
++				spin_lock(&x->lock);
++
++				if (x->curlft.bytes != sa_info_msg.bytes)
++					x->curr_time = ktime_get_real_seconds();
++
++				x->curlft.bytes = sa_info_msg.bytes;
++				x->curlft.packets = sa_info_msg.packets;
++
++				spin_unlock(&x->lock);
++				xfrm_state_put(x);
++			}
++			break;
++
++		case NLKEY_SA_SET_OFFLOAD:
++			memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify));
++			x = xfrm_state_lookup_byhandle(&init_net,sa_notify_msg.sagd);
++			if (x) {
++				spin_lock(&x->lock);
++				if(sa_notify_msg.action)
++					x->offloaded = 	1;
++				else
++					x->offloaded = 	0;
++				spin_unlock(&x->lock);
++				xfrm_state_put(x);
++			}
++			break;
++		default:
++			//printk(KERN_INFO "ipsec_nlkey_rcv fcode 0x%x not supported\n", msg->fcode);
++			break;
++	}
++
++}
++static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, 
++				const struct km_event *c, struct nlkey_sa_id *said)
++{
++
++	struct flowi fl;
++	int tos;
++	xfrm_address_t saddr, daddr;
++	struct dst_entry *dst;
++	struct rt6_info  *rt;
++	int rc = 0;
++	int oif = 0;
++
++	memset(&fl, 0, sizeof(struct flowi));
++
++	/* SPI */
++	said->spi = x->id.spi;
++	/* SA Type (AH or ESP) */
++	said->sa_type = x->id.proto;
++	/* Protocol Family (IPv4 or IPv6) */
++	said->proto_family = x->props.family;
++	/* Replay window */
++	said->replay_window = x->props.replay_window;
++	/* Destination IP Address */
++	if(x->props.family == AF_INET6) {
++		memcpy(&said->dst_ip, x->id.daddr.a6, sizeof(struct in6_addr));
++		fl.u.ip6.daddr = *(struct in6_addr *)x->id.daddr.a6;
++		memcpy(&said->src_ip, x->props.saddr.a6, sizeof(struct in6_addr));
++	}
++	else {
++		said->dst_ip[0] = x->id.daddr.a4;
++		fl.u.ip4.daddr = x->id.daddr.a4;
++		said->src_ip[0] = x->props.saddr.a4;
++	}
++	said->mtu = 0;
++
++	if(x->props.flags & XFRM_STATE_ESN)
++		said->flags = NLKEY_SAFLAGS_ESN;
++	xfrm_flowi_addr_get(&fl, &saddr, &daddr, x->props.family);
++
++	tos = xfrm_get_tos(&fl, x->props.family);
++	if (tos < 0) {
++		printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__);	
++		rc = -1;
++		goto error;
++	}
++	
++	switch (x->props.family)
++	{
++		case AF_INET:
++			if (!__ip_route_output_key(net, &(fl.u.ip4)))
++			{
++				printk(KERN_ERR "%s:%d:  FIXME\n",__func__,__LINE__);
++				rc = -1;
++				goto error;
++			}
++			oif = fl.u.ip4.flowi4_oif;
++			break;
++
++		case AF_INET6:
++			rt = rt6_lookup(net, &fl.u.ip6.daddr, NULL, 0, NULL, 0);
++			if ((!rt) || (!rt->dst.dev))
++			{
++				printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__);
++				rc = -1;
++				goto error;
++			}
++			oif = rt->dst.dev->ifindex;
++			break;
++	}
++
++	{
++		struct xfrm_dst_lookup_params params = {
++			.net = net,
++			.dscp = inet_dsfield_to_dscp(tos),
++			.oif = oif,
++			.saddr = NULL,
++			.daddr = &daddr,
++			.mark = xfrm_smark_get(0, x),
++		};
++		dst = __xfrm_dst_lookup(x->props.family, &params);
++	}
++	if (IS_ERR(dst)) {
++		printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__);
++		rc = -1;
++		goto error;
++	}
++
++	if (strcmp(dst->dev->name, "lo") == 0)
++		said->flags |= NLKEY_SAFLAGS_INBOUND;
++
++	said->dev_mtu = dst_mtu(dst);
++	said->mtu = xfrm_state_mtu(x,dst_mtu(dst));	
++
++	dst_release(dst);
++error:
++	return rc;
++}
++
++static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, 
++					const struct km_event *c, unsigned short *msg_id)
++{
++	struct nlkey_sa_id sa_id_msg;
++	struct nlkey_sa_create sa_create_msg;
++	struct nlkey_sa_delete sa_delete_msg;
++	struct nlkey_sa_set_keys sa_set_keys_msg;
++	struct nlkey_sa_set_tunnel sa_set_tunnel_msg;
++	struct nlkey_sa_set_natt sa_set_natt_msg;
++	struct nlkey_sa_set_state sa_set_state_msg;
++	struct nlkey_sa_set_lifetime sa_set_lifetime_msg;
++	struct nlkey_msg msg;
++	struct sk_buff *skb = NULL;
++	struct nlmsghdr *nlh = NULL;
++	gfp_t allocation = GFP_ATOMIC; //This may called from atomic context
++	unsigned char tunnel, keys, natt, state, lifetime;
++
++	/* supported SA informations */
++	keys = 1; state = 1; tunnel = 1; lifetime = 1; natt = 1; 
++
++	/* next message to build */
++	memset(&msg, 0, sizeof(struct nlkey_msg));
++	msg.fcode = *msg_id;
++	
++	//printk(KERN_INFO "\n\nipsec_xfrm2nlkey: processing event 0x%x\n", msg.fcode);
++
++	switch (msg.fcode)
++	{
++		case NLKEY_SA_CREATE:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_CREATE\n");
++			if(x) {
++				/* some check before builing message */
++				if(x->id.proto != IPPROTO_ESP) {
++					printk(KERN_ERR "protocol %d not supported in fast path.\n", x->id.proto);
++					*msg_id = NLKEY_NULL_MSG;
++					goto exit;
++				}	
++
++				memset(&sa_create_msg, 0, sizeof(struct nlkey_sa_create));
++
++				/* SA global handler */
++				sa_create_msg.sagd = x->handle;
++
++				sa_create_msg.parent_sa_sagd = x->parent_sa_handle;
++
++				/* SA identifier */
++				if(ipsec_nlkey_set_said(net, x, c, &sa_create_msg.said) < 0)
++				{
++					printk(KERN_ERR "%s: set sa ID failed\n", __func__);
++					*msg_id = NLKEY_NULL_MSG; /* next message */
++					goto exit;
++				}
++				memcpy(msg.payload, &sa_create_msg, sizeof(struct nlkey_sa_create));
++				msg.length = sizeof(struct nlkey_sa_create);
++				*msg_id = NLKEY_SA_SET_KEYS; /* next message */
++			} else {
++				*msg_id = NLKEY_NULL_MSG; /* next message */
++				goto exit;
++			}
++			
++			break;
++
++		case NLKEY_SA_SET_KEYS:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_KEYS\n");
++			if(keys) {
++				memset(&sa_set_keys_msg, 0, sizeof(struct nlkey_sa_set_keys));
++
++				/* SA global handler */
++				sa_set_keys_msg.sagd = x->handle; 
++				
++				/* auth key */
++				if(x->aalg) {
++					if (x->aalg->alg_key_len) {
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aalg->alg_key_len;
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.aalgo;
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 0;
++						memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aalg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8));
++						//printk(KERN_INFO "ipsec_xfrm2nlkey: AUTH - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits);
++						sa_set_keys_msg.num_keys++;
++					}
++				}
++				/* encrypt key */
++				if(x->ealg) {
++					if (x->ealg->alg_key_len) {
++
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->ealg->alg_key_len;
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.ealgo;
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1;
++						memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->ealg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8));
++						//printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits);
++						sa_set_keys_msg.num_keys++;
++					}
++				}
++				/* combined key */
++				if (x->aead) {
++					if (x->aead->alg_key_len) {
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aead->alg_key_len;
++						if (strstr(x->aead->alg_name, "rfc4106(gcm"))	/* AES GCM support */
++						{
++							if (x->aead->alg_icv_len == 64)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV8;
++							else if (x->aead->alg_icv_len == 96)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV12;
++							else if (x->aead->alg_icv_len == 128)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV16;
++						}
++						else if (strstr(x->aead->alg_name, "ccm")) /* AES CCM */
++						{
++							if (x->aead->alg_icv_len == 64)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV8;
++							else if (x->aead->alg_icv_len == 96)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV12;
++							else if (x->aead->alg_icv_len == 128)
++								sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV16;
++						}
++						else if (strstr(x->aead->alg_name, "rfc4543(gcm"))	/* AES GMAC defined in RFC 4543 derived from AES GCM */
++						{
++							sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_NULL_AES_GMAC;
++						}
++						
++						sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1;
++						memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aead->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits/ 8));
++						/*
++						printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT -alg name %s algo %d key %d bits\n",
++							x->aead->alg_name, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); 
++						*/
++						sa_set_keys_msg.num_keys++;
++					}
++				}
++	
++				memcpy(msg.payload, &sa_set_keys_msg, sizeof(struct nlkey_sa_set_keys));
++				msg.length = sizeof(struct nlkey_sa_set_keys);
++				*msg_id = NLKEY_SA_SET_TUNNEL; /* next message */
++			} else {
++				*msg_id = NLKEY_SA_SET_TUNNEL; /* next message */
++				goto exit;
++			}
++			break;
++
++		case NLKEY_SA_SET_TUNNEL:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_TUNNEL\n");
++			if(tunnel && (x->props.mode == XFRM_MODE_TUNNEL)) {
++				memset(&sa_set_tunnel_msg, 0, sizeof(struct nlkey_sa_set_tunnel));
++
++				/* SA global handler */
++				sa_set_tunnel_msg.sagd = x->handle; 
++
++				/* Tunnel */
++				sa_set_tunnel_msg.proto_family = x->props.family;
++				if(x->props.family == AF_INET6) {
++					struct ipv6hdr *top_iph = &sa_set_tunnel_msg.h.ipv6h;
++					int dsfield;
++					top_iph->version = 6;
++					top_iph->priority = 0;
++					top_iph->flow_lbl[0] = 0;
++					top_iph->flow_lbl[1] = 0;
++					top_iph->flow_lbl[2] = 0;
++					top_iph->nexthdr = IPPROTO_IPIP;	
++					dsfield = ipv6_get_dsfield(top_iph);
++					dsfield = INET_ECN_encapsulate(dsfield, dsfield);
++					if (x->props.flags & XFRM_STATE_NOECN)
++						dsfield &= ~INET_ECN_MASK;
++					ipv6_change_dsfield(top_iph, 0, dsfield);
++					top_iph->hop_limit = 64;
++					memcpy(&top_iph->daddr, x->id.daddr.a6, sizeof(struct in6_addr));
++					memcpy(&top_iph->saddr, x->props.saddr.a6, sizeof(struct in6_addr));
++					//printk(KERN_INFO "ipsec_xfrm2nlkey: IPv6 tunnel\n");
++					//printk(KERN_INFO "dst: %x %x %x %x\n", x->id.daddr.a6[0], x->id.daddr.a6[1], x->id.daddr.a6[2], x->id.daddr.a6[3]);
++					//(KERN_INFO "src: %x %x %x %x\n", x->props.saddr.a6[0], x->props.saddr.a6[1], x->props.saddr.a6[2], x->props.saddr.a6[3]);
++				}
++				else {
++					struct iphdr *top_iph = &sa_set_tunnel_msg.h.ipv4h;
++					top_iph->ihl = 5;
++					top_iph->version = 4;
++					top_iph->tos = 0;
++					top_iph->frag_off = 0; 
++					top_iph->ttl = 64;
++					top_iph->saddr = x->props.saddr.a4;
++					top_iph->daddr = x->id.daddr.a4;
++					//printk(KERN_INFO "ipsec_xfrm2nlkey: IPv4 tunnel dst:%x - src:%x \n", x->id.daddr.a4, x->props.saddr.a4);
++				}
++				memcpy(msg.payload, &sa_set_tunnel_msg, sizeof(struct nlkey_sa_set_tunnel));
++				msg.length = sizeof(struct nlkey_sa_set_tunnel);
++				*msg_id = NLKEY_SA_SET_NATT; /* next message */
++			} else {
++				*msg_id = NLKEY_SA_SET_NATT; /* next message */
++				goto exit;	
++			} 
++			break;
++
++		case NLKEY_SA_SET_NATT:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_NATT\n");
++			if((natt) && (x->encap)){
++				memset(&sa_set_natt_msg, 0, sizeof(struct nlkey_sa_set_natt));
++
++				/* SA global handler */
++				sa_set_natt_msg.sagd = x->handle; 
++				sa_set_natt_msg.sport = x->encap->encap_sport;
++				sa_set_natt_msg.dport = x->encap->encap_dport;
++				//printk(KERN_INFO "src port: %d  dst port: %d \n", ntohs(sa_set_natt_msg.sport), ntohs( sa_set_natt_msg.dport));
++				memcpy(msg.payload, &sa_set_natt_msg, sizeof(struct nlkey_sa_set_natt));
++				msg.length = sizeof(struct nlkey_sa_set_natt);
++				*msg_id = NLKEY_SA_SET_LIFETIME; /* next message */
++			} else {
++				*msg_id = NLKEY_SA_SET_LIFETIME; /* next message */
++				goto exit;	
++			}
++			break;
++
++		case NLKEY_SA_SET_LIFETIME:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_LIFETIME\n");
++			if(lifetime) {
++				memset(&sa_set_lifetime_msg, 0, sizeof(struct nlkey_sa_set_lifetime));
++
++				/* SA global handler */
++				sa_set_lifetime_msg.sagd = x->handle;
++
++				/* hard time */
++				sa_set_lifetime_msg.hard_time.allocations =  _X2KEY(x->lft.hard_packet_limit);
++				if(_X2KEY(x->lft.hard_byte_limit))
++					memcpy(sa_set_lifetime_msg.hard_time.bytes, &x->lft.hard_byte_limit, sizeof(uint64_t));
++
++				/* soft time */
++				sa_set_lifetime_msg.soft_time.allocations =  _X2KEY(x->lft.soft_packet_limit);
++				if(_X2KEY(x->lft.soft_byte_limit))
++					memcpy(sa_set_lifetime_msg.soft_time.bytes, &x->lft.soft_byte_limit, sizeof(uint64_t));
++
++				/* current time */
++				sa_set_lifetime_msg.current_time.allocations = x->curlft.packets;
++				memcpy(sa_set_lifetime_msg.current_time.bytes, &x->curlft.bytes, sizeof(uint64_t));
++
++				memcpy(msg.payload, &sa_set_lifetime_msg, sizeof(struct nlkey_sa_set_lifetime));
++				msg.length = sizeof(struct nlkey_sa_set_lifetime);
++				*msg_id = NLKEY_SA_SET_STATE; /* next message */
++			} else {
++				*msg_id = NLKEY_SA_SET_STATE; /* next message */
++				goto exit;	
++			}
++			break;
++
++		case NLKEY_SA_SET_STATE:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SET_STATE\n");
++			if(state) {
++				memset(&sa_set_state_msg, 0, sizeof(struct nlkey_sa_set_state));
++				memset(&sa_id_msg, 0, sizeof(struct nlkey_sa_id));
++
++				/* SA global handler */
++				sa_set_state_msg.sagd = x->handle; 
++				sa_set_state_msg.parent_sa_sagd = x->parent_sa_handle;
++				/* State */
++				sa_set_state_msg.state = x->km.state;
++				// TODO: set the offloaded state once ack received !
++
++				memcpy(msg.payload, &sa_set_state_msg, sizeof(struct nlkey_sa_set_state));
++				msg.length = sizeof(struct nlkey_sa_set_state);
++				*msg_id = NLKEY_NULL_MSG; /* next message */
++			} else {
++				*msg_id = NLKEY_NULL_MSG; /* next message */
++				goto exit;
++			}
++			break;
++		
++		case NLKEY_SA_DELETE:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_DELETE\n");
++			memset(&sa_delete_msg, 0, sizeof(struct nlkey_sa_delete));
++			
++			/* SA global handler */
++			sa_delete_msg.sagd = x->handle;
++			memcpy(msg.payload, &sa_delete_msg, sizeof(struct nlkey_sa_delete));
++			msg.length = sizeof(struct nlkey_sa_delete);
++
++
++			*msg_id = NLKEY_NULL_MSG; /* next message */
++			break;
++
++		case NLKEY_SA_FLUSH:
++			//printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_FLUSH\n");
++			/* No data required for flush SA command */
++
++			*msg_id = NLKEY_NULL_MSG; /* next message */
++			break;
++
++		default:
++			printk(KERN_ERR "ipsec_xfrm2nlkey: event 0x%x not supported\n", c->event);
++			*msg_id = NLKEY_NULL_MSG; /* next message */
++			break;
++	}
++
++	/* prepare netlink message for kernel to user space direction */
++	if(msg.length > NLKEY_MSG_LEN)
++	{
++		printk(KERN_ERR "ipsec_xfrm2nlkey: maximum message size reached (%d bytes)\n", msg.length);
++		goto exit;
++	}
++
++	skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
++	if (skb == NULL)
++		goto exit;
++		
++	nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
++	memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
++	
++	/* whole length of the message i.e. header + payload */
++	nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
++
++	/* from kernel */
++	nlh->nlmsg_pid = 0;
++	nlh->nlmsg_flags = 0;
++        nlh->nlmsg_type = 0;
++	NETLINK_CB(skb).portid = 0;
++	NETLINK_CB(skb).dst_group = 1;
++exit:
++	return skb;
++}
++
++static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c)
++{
++	struct sk_buff *skb;
++	unsigned short msg_type;
++	int rc = 0;
++
++	/* We may generate more than one message when adding new SA (sa_create + sa_set_state + sa_set_tunnel...) */
++	msg_type = ipsec_sacode_to_nlkeycode((unsigned short)c->event);
++
++	while(msg_type != NLKEY_NULL_MSG)
++	{
++		/* build nlkey message */
++		skb = ipsec_xfrm2nlkey(net, x, c, &msg_type);
++
++		if(skb != NULL)
++			if((rc = netlink_broadcast(nlkey_socket, skb, 0, 1, GFP_ATOMIC)) < 0)
++				return rc;
++	}
++
++	return rc;
++}
++
++
++int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh)
++{
++	struct sk_buff *skb;
++	struct nlkey_msg msg;
++	struct nlmsghdr *nlh = NULL;
++	unsigned short *p;
++	gfp_t allocation = GFP_ATOMIC; //This may called from atomic context
++
++	//printk(KERN_INFO "ipsec_nlkey_flow \n");
++
++	/* next message to build */
++	memset(&msg, 0, sizeof(struct nlkey_msg));
++	msg.fcode = NLKEY_FLOW_ADD;
++
++	// Number of SA for this flow
++	p = msg.payload;
++	*p++ = xfrm_nr;
++	msg.length += sizeof(unsigned short);
++	// SA handles list
++	memcpy(p, xfrm_handle, xfrm_nr*sizeof(unsigned short));
++	msg.length += xfrm_nr*sizeof(unsigned short);
++	p+=xfrm_nr;
++	// flow family
++	*p++ = family;
++	msg.length += sizeof(unsigned short);
++	// flow family
++	*p++ = dir;
++	msg.length += sizeof(unsigned short);
++	// flow mode
++	*p++ = ignore_neigh;
++	msg.length += sizeof(unsigned short);
++	// flow descriptor
++	memcpy(p, fl, sizeof(struct flowi));
++	msg.length +=sizeof(struct flowi);
++	p+=sizeof(struct flowi) / sizeof(u16);
++
++	skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
++	if (skb == NULL)
++		return -ENOMEM;
++
++	/* prepare netlink message for kernel to user space direction */
++	nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
++	memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
++
++	/* whole length of the message i.e. header + payload */
++	nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
++
++	/* from kernel */
++	nlh->nlmsg_pid = 0; 
++	nlh->nlmsg_flags = 0;
++        nlh->nlmsg_type = 0;
++	NETLINK_CB(skb).portid = 0;
++	NETLINK_CB(skb).dst_group = 1;
++
++	return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation));
++}
++EXPORT_SYMBOL(ipsec_nlkey_flow);
++
++
++int ipsec_nlkey_flow_remove(struct flowi *fl, u16 family, u16 dir)
++{
++	struct sk_buff *skb;
++	struct nlkey_msg msg;
++	struct nlmsghdr *nlh = NULL;
++	unsigned short *p;
++	gfp_t allocation = GFP_ATOMIC; //This may called from atomic context
++
++	
++	//printk(KERN_INFO "ipsec_nlkey_flow_remove\n");
++
++	/* next message to build */
++	memset(&msg, 0, sizeof(struct nlkey_msg));
++	msg.fcode = NLKEY_FLOW_REMOVE;
++
++	p = msg.payload;
++	// flow family
++	*p++ = family;
++	msg.length += sizeof(unsigned short);
++	// flow family
++	*p++ = dir;
++	msg.length += sizeof(unsigned short);
++	// flow descriptor
++	memcpy(p, fl, sizeof(struct flowi));
++	msg.length +=sizeof(struct flowi);
++	p+=sizeof(struct flowi) / sizeof(u16);
++
++	skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation);
++	if (skb == NULL)
++		return -ENOMEM;
++
++	/* prepare netlink message for kernel to user space direction */
++	nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length));
++	memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length));
++	
++	/* whole length of the message i.e. header + payload */
++	nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length);
++
++	/* from kernel */
++	nlh->nlmsg_pid = 0; 
++	nlh->nlmsg_flags = 0;
++        nlh->nlmsg_type = 0;
++	NETLINK_CB(skb).portid = 0;
++	NETLINK_CB(skb).dst_group = 1;	
++
++		
++        return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation));
++
++	
++}
++EXPORT_SYMBOL(ipsec_nlkey_flow_remove);
++
++
++
++static void ipsec_nlkey_init(void)
++{
++	struct netlink_kernel_cfg cfg = {
++		.groups	  = 1,
++		.input	  = ipsec_nlkey_rcv,
++	};
++	printk(KERN_INFO "Initializing NETLINK_KEY socket\n");
++	nlkey_socket = netlink_kernel_create(&init_net, NETLINK_KEY, &cfg);
++}
++#endif
++
++
+ static int __net_init pfkey_net_init(struct net *net)
+ {
+ 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
+@@ -3897,6 +4772,11 @@ static void __exit ipsec_pfkey_exit(void)
+ 	sock_unregister(PF_KEY);
+ 	unregister_pernet_subsys(&pfkey_net_ops);
+ 	proto_unregister(&key_proto);
++
++#ifdef NLKEY_SUPPORT
++	/* release NETLINK_KEY socket */
++	sock_release(nlkey_socket->sk_socket);
++#endif
+ }
+ 
+ static int __init ipsec_pfkey_init(void)
+@@ -3913,6 +4793,12 @@ static int __init ipsec_pfkey_init(void)
+ 	if (err != 0)
+ 		goto out_unregister_pernet;
+ 	xfrm_register_km(&pfkeyv2_mgr);
++
++#ifdef NLKEY_SUPPORT
++	/* create NETLINK_KEY socket for IPSec offload on Comcerto */
++	ipsec_nlkey_init();
++#endif
++
+ out:
+ 	return err;
+ 
diff --git a/patches/ask/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch b/patches/ask/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch
new file mode 100644
index 0000000..c8e98cf
--- /dev/null
+++ b/patches/ask/kernel/0124-net__netfilter__nf_conntrack_netlink.c.patch
@@ -0,0 +1,288 @@
+diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
+index 3a04665..7e7d13d 100644
+--- a/net/netfilter/nf_conntrack_netlink.c
++++ b/net/netfilter/nf_conntrack_netlink.c
+@@ -28,6 +28,11 @@
+ #include <linux/netlink.h>
+ #include <linux/spinlock.h>
+ #include <linux/interrupt.h>
++#if defined(CONFIG_CPE_FAST_PATH)
++#ifndef IPSEC_FLOW_CACHE
++#include <net/xfrm.h>
++#endif
++#endif
+ #include <linux/slab.h>
+ #include <linux/siphash.h>
+ 
+@@ -202,9 +207,16 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct,
+ 	struct nlattr *nest_proto;
+ 	int ret;
+ 
++#ifdef CONFIG_CPE_FAST_PATH
++	rcu_read_lock();
++#endif
+ 	l4proto = nf_ct_l4proto_find(nf_ct_protonum(ct));
+-	if (!l4proto->to_nlattr)
++	if (!l4proto->to_nlattr) {
++#ifdef CONFIG_CPE_FAST_PATH
++		rcu_read_unlock();
++#endif
+ 		return 0;
++	}
+ 
+ 	nest_proto = nla_nest_start(skb, CTA_PROTOINFO);
+ 	if (!nest_proto)
+@@ -214,9 +226,15 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct,
+ 
+ 	nla_nest_end(skb, nest_proto);
+ 
++#ifdef CONFIG_CPE_FAST_PATH
++	rcu_read_unlock();
++#endif
+ 	return ret;
+ 
+ nla_put_failure:
++#ifdef CONFIG_CPE_FAST_PATH
++	rcu_read_unlock();
++#endif
+ 	return -1;
+ }
+ 
+@@ -353,6 +371,18 @@ nla_put_failure:
+ #define ctnetlink_dump_mark(a, b, c) (0)
+ #endif
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++static inline int
++ctnetlink_dump_qosconnmark(struct sk_buff *skb, const struct nf_conn *ct)
++{
++	nla_put_be64(skb, CTA_QOSCONNMARK, cpu_to_be64(ct->qosconnmark),
++		     CTA_QOSCONNMARK_PAD);
++	return 0;
++}
++#else
++#define ctnetlink_dump_qosconnmark(a, b) (0)
++#endif
++
+ #ifdef CONFIG_NF_CONNTRACK_SECMARK
+ static int ctnetlink_dump_secctx(struct sk_buff *skb, const struct nf_conn *ct)
+ {
+@@ -430,6 +460,59 @@ ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct)
+ 	return 0;
+ }
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++static int
++ctnetlink_dump_comcerto_fp(struct sk_buff *skb, const struct nf_conn *ct)
++{
++	struct nlattr *nest_count;
++
++	nest_count = nla_nest_start(skb, CTA_LAYERSCAPE_FP_ORIG | NLA_F_NESTED);
++	if (!nest_count)
++		goto nla_put_failure;
++
++	nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_ORIGINAL].mark);
++	nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_ORIGINAL].ifindex);
++	nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].iif);
++	nla_put_u32(skb, CTA_COMCERTO_FP_UNDERLYING_IIF, ct->fp_info[IP_CT_DIR_ORIGINAL].underlying_iif);
++	nla_put_u16(skb, CTA_COMCERTO_FP_UNDERLYING_VID, ct->fp_info[IP_CT_DIR_ORIGINAL].underlying_vlan_id);
++#ifndef IPSEC_FLOW_CACHE
++	if ((ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle[0]) ||
++	    (ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle[MAX_SUPPORTED_XFRMS_PER_DIR]))
++	{
++		nla_put(skb, CTA_COMCERTO_FP_XFRM_HANDLE, sizeof(ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle),
++				ct->fp_info[IP_CT_DIR_ORIGINAL].xfrm_handle);
++	}
++#endif /* IPSEC_FLOW_CACHE */
++	nla_nest_end(skb, nest_count);
++
++	nest_count = nla_nest_start(skb, CTA_LAYERSCAPE_FP_REPLY | NLA_F_NESTED);
++	if (!nest_count)
++		goto nla_put_failure;
++
++	nla_put_u32(skb, CTA_COMCERTO_FP_MARK, ct->fp_info[IP_CT_DIR_REPLY].mark);
++	nla_put_u32(skb, CTA_COMCERTO_FP_IFINDEX, ct->fp_info[IP_CT_DIR_REPLY].ifindex);
++	nla_put_u32(skb, CTA_COMCERTO_FP_IIF, ct->fp_info[IP_CT_DIR_REPLY].iif);
++	nla_put_u32(skb, CTA_COMCERTO_FP_UNDERLYING_IIF, ct->fp_info[IP_CT_DIR_REPLY].underlying_iif);
++	nla_put_u16(skb, CTA_COMCERTO_FP_UNDERLYING_VID, ct->fp_info[IP_CT_DIR_REPLY].underlying_vlan_id);
++#ifndef IPSEC_FLOW_CACHE
++	if ((ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle[0]) ||
++	    (ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle[MAX_SUPPORTED_XFRMS_PER_DIR]))
++	{
++		nla_put(skb, CTA_COMCERTO_FP_XFRM_HANDLE, sizeof(ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle),
++			ct->fp_info[IP_CT_DIR_REPLY].xfrm_handle);
++	}
++#endif /* IPSEC_FLOW_CACHE */
++	nla_nest_end(skb, nest_count);
++
++	return 0;
++
++nla_put_failure:
++	return -1;
++}
++#else
++#define ctnetlink_dump_comcerto_fp(a, b) (0)
++#endif
++
+ #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
+ 
+ static int ctnetlink_dump_master(struct sk_buff *skb, const struct nf_conn *ct)
+@@ -570,7 +653,11 @@ static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct)
+ {
+ 	if (ctnetlink_dump_status(skb, ct) < 0 ||
+ 	    ctnetlink_dump_mark(skb, ct, true) < 0 ||
++	    ctnetlink_dump_qosconnmark(skb, ct) < 0 ||
+ 	    ctnetlink_dump_secctx(skb, ct) < 0 ||
++#ifdef CONFIG_CPE_FAST_PATH
++	    ctnetlink_dump_comcerto_fp(skb, ct) < 0 ||
++#endif
+ 	    ctnetlink_dump_id(skb, ct) < 0 ||
+ 	    ctnetlink_dump_use(skb, ct) < 0 ||
+ 	    ctnetlink_dump_master(skb, ct) < 0)
+@@ -722,6 +809,13 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct)
+ 	       + nla_total_size(0) /* CTA_HELP */
+ 	       + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+ 	       + ctnetlink_secctx_size(ct)
++#ifdef CONFIG_CPE_FAST_PATH
++			+ 2 * nla_total_size(0) /* CTA_LAYERSCAPE_FP_ORIG|REPL */
++			+ 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_MARK */
++			+ 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IFINDEX */
++			+ 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_IIF */
++			+ 2 * nla_total_size(sizeof(uint32_t)) /* CTA_COMCERTO_FP_UNDERLYING_IIF */
++#endif
+ #if IS_ENABLED(CONFIG_NF_NAT)
+ 	       + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+ 	       + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
+@@ -729,6 +823,9 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct)
+ #ifdef CONFIG_NF_CONNTRACK_MARK
+ 	       + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
+ #endif
++#if defined(CONFIG_CPE_FAST_PATH)
++			+ nla_total_size(sizeof(u_int64_t)) /* CTA_QOSCONNMARK */
++#endif
+ #ifdef CONFIG_NF_CONNTRACK_ZONES
+ 	       + nla_total_size(sizeof(u_int16_t)) /* CTA_ZONE|CTA_TUPLE_ZONE */
+ #endif
+@@ -806,6 +903,11 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
+ 				   NF_CT_DEFAULT_ZONE_DIR) < 0)
+ 		goto nla_put_failure;
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++	if (ctnetlink_dump_comcerto_fp(skb, ct) < 0)
++		goto nla_put_failure;
++#endif
++
+ 	if (ctnetlink_dump_id(skb, ct) < 0)
+ 		goto nla_put_failure;
+ 
+@@ -858,6 +960,11 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
+ 	if (ctnetlink_dump_mark(skb, ct, events & (1 << IPCT_MARK)))
+ 		goto nla_put_failure;
+ #endif
++#if defined(CONFIG_CPE_FAST_PATH)
++	if ((events & (1 << IPCT_QOSCONNMARK) || ct->qosconnmark) &&
++	    ctnetlink_dump_qosconnmark(skb, ct) < 0)
++		goto nla_put_failure;
++#endif
+ 
+ 	if (ctnetlink_dump_event_timestamp(skb, ct))
+ 		goto nla_put_failure;
+@@ -1570,6 +1677,9 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
+ 	[CTA_NAT_SRC]		= { .type = NLA_NESTED },
+ 	[CTA_TIMEOUT] 		= { .type = NLA_U32 },
+ 	[CTA_MARK]		= { .type = NLA_U32 },
++#if defined(CONFIG_CPE_FAST_PATH)
++	[CTA_QOSCONNMARK]		= { .type = NLA_U64 },
++#endif
+ 	[CTA_ID]		= { .type = NLA_U32 },
+ 	[CTA_NAT_DST]		= { .type = NLA_NESTED },
+ 	[CTA_TUPLE_MASTER]	= { .type = NLA_NESTED },
+@@ -1906,6 +2016,48 @@ ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[])
+ 	return nf_ct_change_status_common(ct, ntohl(nla_get_be32(cda[CTA_STATUS])));
+ }
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++/*
++ * This function detects ctnetlink messages that require
++ * to set the conntrack status to IPS_PERMANENT.
++ * It updates only this bit regardless of other possible
++ * changes.
++ * Return 0 if succesfull
++ */
++static int
++ctnetlink_change_permanent(struct nf_conn *ct, const struct nlattr * const cda[])
++{
++	unsigned int status;
++	u_int32_t id;
++	__be32 conntrack_id = ntohl((__force __be32)nf_ct_get_id(ct));
++
++	if (cda[CTA_STATUS] && cda[CTA_ID]) {
++		status = ntohl(nla_get_be32(cda[CTA_STATUS]));
++		id = ntohl(nla_get_be32(cda[CTA_ID]));
++
++		if (status & IPS_PERMANENT) {
++			if (conntrack_id == id) {
++				ct->status |= IPS_PERMANENT;
++				return 0;
++			}
++			else
++				return -ENOENT;
++		}
++		else if (nf_ct_is_permanent(ct))
++		{
++			/* Clear the PERMANENT bit. */
++			if (conntrack_id == id) {
++				clear_bit(IPS_PERMANENT_BIT, &ct->status);
++				return 0;
++			}
++			else
++				return -ENOENT;
++		}
++	}
++	return -1;
++}
++#endif
++
+ static int
+ ctnetlink_setup_nat(struct nf_conn *ct, const struct nlattr * const cda[])
+ {
+@@ -2209,6 +2361,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
+ 			return err;
+ 	}
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++	if (cda[CTA_QOSCONNMARK])
++		ct->qosconnmark = be64_to_cpu(nla_get_be64(cda[CTA_QOSCONNMARK]));
++#endif
++
+ #if defined(CONFIG_NF_CONNTRACK_MARK)
+ 	if (cda[CTA_MARK])
+ 		ctnetlink_change_mark(ct, cda);
+@@ -2347,6 +2504,11 @@ ctnetlink_create_conntrack(struct net *net,
+ 			goto err2;
+ 	}
+ 
++#if defined(CONFIG_CPE_FAST_PATH)
++	if (cda[CTA_QOSCONNMARK])
++		ct->qosconnmark = be64_to_cpu(nla_get_be64(cda[CTA_QOSCONNMARK]));
++#endif
++
+ #if defined(CONFIG_NF_CONNTRACK_MARK)
+ 	if (cda[CTA_MARK])
+ 		ctnetlink_change_mark(ct, cda);
+@@ -2473,6 +2635,15 @@ static int ctnetlink_new_conntrack(struct sk_buff *skb,
+ 	err = -EEXIST;
+ 	ct = nf_ct_tuplehash_to_ctrack(h);
+ 	if (!(info->nlh->nlmsg_flags & NLM_F_EXCL)) {
++#if defined(CONFIG_CPE_FAST_PATH)
++		/* If the permanent status has been set, this is a specific
++		 * message. Don't broadcast the event and don't update the ct */
++		err = ctnetlink_change_permanent(ct, cda);
++		if ((err == 0) || (err == -ENOENT)) {
++			nf_ct_put(ct);
++			return err;
++		}
++#endif
+ 		err = ctnetlink_change_conntrack(ct, cda);
+ 		if (err == 0) {
+ 			nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
diff --git a/patches/ask/kernel/0129-net__wireless__Kconfig.patch b/patches/ask/kernel/0129-net__wireless__Kconfig.patch
new file mode 100644
index 0000000..468514c
--- /dev/null
+++ b/patches/ask/kernel/0129-net__wireless__Kconfig.patch
@@ -0,0 +1,21 @@
+diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
+index 5d6a0b2b4f3a..7c3d10e2e8b1 100644
+--- a/net/wireless/Kconfig
++++ b/net/wireless/Kconfig
+@@ -1,6 +1,6 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ config WIRELESS_EXT
+-	bool
++	def_bool y
+ 
+ config WEXT_CORE
+ 	def_bool y
+@@ -11,7 +11,7 @@ config WEXT_PROC
+ 	depends on WEXT_CORE
+ 
+ config WEXT_PRIV
+-	bool
++	def_bool y
+ 
+ config CFG80211
+ 	tristate "cfg80211 - wireless configuration API"
diff --git a/patches/ask/kernel/0131-net__xfrm__Makefile.patch b/patches/ask/kernel/0131-net__xfrm__Makefile.patch
new file mode 100644
index 0000000..7183410
--- /dev/null
+++ b/patches/ask/kernel/0131-net__xfrm__Makefile.patch
@@ -0,0 +1,9 @@
+diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile
+index 0a0a0a0..0b0b0b0 100644
+--- a/net/xfrm/Makefile
++++ b/net/xfrm/Makefile
+@@ -24,3 +24,4 @@ obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o
+ obj-$(CONFIG_XFRM_IPTFS) += xfrm_iptfs.o
+ obj-$(CONFIG_XFRM_ESPINTCP) += espintcp.o
+ obj-$(CONFIG_DEBUG_INFO_BTF) += xfrm_state_bpf.o
++obj-$(CONFIG_INET_IPSEC_OFFLOAD) += ipsec_flow.o
diff --git a/patches/ask/kernel/0136-net__xfrm__xfrm_policy.c.patch b/patches/ask/kernel/0136-net__xfrm__xfrm_policy.c.patch
new file mode 100644
index 0000000..30460f8
--- /dev/null
+++ b/patches/ask/kernel/0136-net__xfrm__xfrm_policy.c.patch
@@ -0,0 +1,163 @@
+diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
+index 62486f8..3cbe4f8 100644
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -48,6 +48,11 @@
+ #include <net/inet_dscp.h>
+ 
+ #include "xfrm_hash.h"
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++#include "ipsec_flow.h"
++#endif
++#endif
+ 
+ #define XFRM_QUEUE_TMO_MIN ((unsigned)(HZ/10))
+ #define XFRM_QUEUE_TMO_MAX ((unsigned)(60*HZ))
+@@ -179,6 +184,15 @@ static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
+ 
+ static struct kmem_cache *xfrm_dst_cache __ro_after_init;
+ 
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++extern int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle,
++		const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh);
++int ipsec_flow_init(struct net *net);
++void ipsec_flow_fini(struct net *net);
++#endif
++#endif
++
+ static struct rhashtable xfrm_policy_inexact_table;
+ static const struct rhashtable_params xfrm_pol_inexact_params;
+ 
+@@ -2599,6 +2613,17 @@ static dscp_t xfrm_get_dscp(const struct flowi *fl, int family)
+ 	return 0;
+ }
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++int xfrm_get_tos(const struct flowi *fl, int family)
++{
++	if (family == AF_INET)
++		return inet_dscp_to_dsfield(fl->u.ip4.flowi4_dscp) & INET_DSCP_MASK;
++
++	return 0;
++}
++EXPORT_SYMBOL(xfrm_get_tos);
++#endif
++
+ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
+ {
+ 	const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+@@ -3295,6 +3320,37 @@ no_transform:
+ 		dst = dst_orig;
+ 	}
+ 
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	{
++		struct dst_entry *dst1 = dst;
++		struct xfrm_state *x;
++		u16 xfrm_handle[XFRM_POLICY_TYPE_MAX];
++		u16 ignore_neigh = 0;
++
++		num_xfrms = 0;
++		memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16));
++		while (((x = dst1->xfrm) != NULL) &&
++		       (num_xfrms < XFRM_POLICY_TYPE_MAX)) {
++			xfrm_handle[num_xfrms++] = x->handle;
++			if (x->props.mode == XFRM_MODE_TUNNEL)
++				ignore_neigh = 1;
++			dst1 = xfrm_dst_child(dst1);
++
++			if (dst1 == NULL) {
++				err = -EHOSTUNREACH;
++				goto error;
++			}
++		}
++		if (ipsec_flow_add(net, fl, family, dir, xfrm_handle)) {
++			/* sent flow notification to cmm with sa_handle */
++			ipsec_nlkey_flow(num_xfrms, xfrm_handle, fl, family,
++					 (unsigned short)dir, ignore_neigh);
++		}
++	}
++#endif
++#endif
++
+ ok:
+ 	xfrm_pols_put(pols, drop_pols);
+ 	if (dst->xfrm &&
+@@ -3853,6 +3909,34 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
+ 			goto reject;
+ 		}
+ 
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		{
++			struct xfrm_state *x;
++			u16 xfrm_handle[XFRM_POLICY_TYPE_MAX];
++
++			xfrm_nr = 0;
++			memset(xfrm_handle, 0, XFRM_POLICY_TYPE_MAX * sizeof(u16));
++			for (i = sp->len - 1;
++			     (i >= 0) && (xfrm_nr < XFRM_POLICY_TYPE_MAX); i--) {
++				x = sp->xvec[i];
++				xfrm_handle[xfrm_nr++] = x->handle;
++			}
++			if (ipsec_flow_add(net, (const struct flowi *)&fl, family, dir,
++					   xfrm_handle)) {
++				/* sent flow notification to cmm with sa_handle */
++				ipsec_nlkey_flow(xfrm_nr, xfrm_handle,
++						 (const struct flowi *)&fl, family, dir, 0);
++			}
++		}
++
++		/* Hub and spoke changes: Setting the POLICY_IN direction in the packet */
++		skb->ipsec_xfrm_dir |= (1 << XFRM_POLICY_IN);
++
++std_path:
++#endif
++#endif
++
+ 		xfrm_pols_put(pols, npols);
+ 		sp->verified_cnt = k;
+ 
+@@ -4328,6 +4412,14 @@ static int __net_init xfrm_net_init(struct net *net)
+ 	if (rv < 0)
+ 		goto out_sysctl;
+ 
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	rv = ipsec_flow_init(net);
++	if (rv < 0)
++		goto out_ipsec_flow;
++#endif
++#endif
++
+ 	rv = xfrm_nat_keepalive_net_init(net);
+ 	if (rv < 0)
+ 		goto out_nat_keepalive;
+@@ -4335,6 +4427,12 @@ static int __net_init xfrm_net_init(struct net *net)
+ 	return 0;
+ 
+ out_nat_keepalive:
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	ipsec_flow_fini(net);
++out_ipsec_flow:
++#endif
++#endif
+ 	xfrm_sysctl_fini(net);
+ out_sysctl:
+ 	xfrm_policy_fini(net);
+@@ -4349,6 +4447,11 @@ out_statistics:
+ static void __net_exit xfrm_net_exit(struct net *net)
+ {
+ 	xfrm_nat_keepalive_net_fini(net);
++#ifdef IPSEC_FLOW_CACHE
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	ipsec_flow_fini(net);
++#endif
++#endif
+ 	xfrm_sysctl_fini(net);
+ 	xfrm_policy_fini(net);
+ 	xfrm_state_fini(net);
diff --git a/patches/ask/kernel/0137-net__xfrm__xfrm_state.c.patch b/patches/ask/kernel/0137-net__xfrm__xfrm_state.c.patch
new file mode 100644
index 0000000..36db635
--- /dev/null
+++ b/patches/ask/kernel/0137-net__xfrm__xfrm_state.c.patch
@@ -0,0 +1,291 @@
+diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
+index 9e14e45..d685ed7 100644
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -58,6 +58,10 @@ static inline bool xfrm_state_hold_rcu(struct xfrm_state __rcu *x)
+ 	return refcount_inc_not_zero(&x->refcnt);
+ }
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++static unsigned short xfrm_state_handle;
++#endif
++
+ static inline unsigned int xfrm_dst_hash(struct net *net,
+ 					 const xfrm_address_t *daddr,
+ 					 const xfrm_address_t *saddr,
+@@ -119,6 +123,9 @@ static void xfrm_hash_transfer(struct hlist_head *list,
+ 			       struct hlist_head *nsrctable,
+ 			       struct hlist_head *nspitable,
+ 			       struct hlist_head *nseqtable,
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++			       struct hlist_head *nhtable,
++#endif
+ 			       unsigned int nhashmask)
+ {
+ 	struct hlist_node *tmp;
+@@ -150,6 +157,13 @@ static void xfrm_hash_transfer(struct hlist_head *list,
+ 			XFRM_STATE_INSERT(byseq, &x->byseq, nseqtable + h,
+ 					  x->xso.type);
+ 		}
++
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		if (x->handle && x->in_byh_hash) {
++			h = x->handle & nhashmask;
++			hlist_add_head_rcu(&x->byh, nhtable + h);
++		}
++#endif
+ 	}
+ }
+ 
+@@ -162,6 +176,9 @@ static void xfrm_hash_resize(struct work_struct *work)
+ {
+ 	struct net *net = container_of(work, struct net, xfrm.state_hash_work);
+ 	struct hlist_head *ndst, *nsrc, *nspi, *nseq, *odst, *osrc, *ospi, *oseq;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	struct hlist_head *nh, *oh;
++#endif
+ 	unsigned long nsize, osize;
+ 	unsigned int nhashmask, ohashmask;
+ 	int i;
+@@ -188,6 +205,16 @@ static void xfrm_hash_resize(struct work_struct *work)
+ 		xfrm_hash_free(nspi, nsize);
+ 		return;
+ 	}
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	nh = xfrm_hash_alloc(nsize);
++	if (!nh) {
++		xfrm_hash_free(ndst, nsize);
++		xfrm_hash_free(nsrc, nsize);
++		xfrm_hash_free(nspi, nsize);
++		xfrm_hash_free(nseq, nsize);
++		return;
++	}
++#endif
+ 
+ 	spin_lock_bh(&net->xfrm.xfrm_state_lock);
+ 	write_seqcount_begin(&net->xfrm.xfrm_state_hash_generation);
+@@ -195,17 +222,27 @@ static void xfrm_hash_resize(struct work_struct *work)
+ 	nhashmask = (nsize / sizeof(struct hlist_head)) - 1U;
+ 	odst = xfrm_state_deref_prot(net->xfrm.state_bydst, net);
+ 	for (i = net->xfrm.state_hmask; i >= 0; i--)
+-		xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq, nhashmask);
++		xfrm_hash_transfer(odst + i, ndst, nsrc, nspi, nseq,
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++				   nh,
++#endif
++				   nhashmask);
+ 
+ 	osrc = xfrm_state_deref_prot(net->xfrm.state_bysrc, net);
+ 	ospi = xfrm_state_deref_prot(net->xfrm.state_byspi, net);
+ 	oseq = xfrm_state_deref_prot(net->xfrm.state_byseq, net);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	oh = xfrm_state_deref_prot(net->xfrm.state_byh, net);
++#endif
+ 	ohashmask = net->xfrm.state_hmask;
+ 
+ 	rcu_assign_pointer(net->xfrm.state_bydst, ndst);
+ 	rcu_assign_pointer(net->xfrm.state_bysrc, nsrc);
+ 	rcu_assign_pointer(net->xfrm.state_byspi, nspi);
+ 	rcu_assign_pointer(net->xfrm.state_byseq, nseq);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	rcu_assign_pointer(net->xfrm.state_byh, nh);
++#endif
+ 	net->xfrm.state_hmask = nhashmask;
+ 
+ 	write_seqcount_end(&net->xfrm.xfrm_state_hash_generation);
+@@ -219,6 +256,9 @@ static void xfrm_hash_resize(struct work_struct *work)
+ 	xfrm_hash_free(osrc, osize);
+ 	xfrm_hash_free(ospi, osize);
+ 	xfrm_hash_free(oseq, osize);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	xfrm_hash_free(oh, osize);
++#endif
+ }
+ 
+ static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
+@@ -744,6 +784,9 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
+ 		INIT_HLIST_NODE(&x->bysrc);
+ 		INIT_HLIST_NODE(&x->byspi);
+ 		INIT_HLIST_NODE(&x->byseq);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		INIT_HLIST_NODE(&x->byh);
++#endif
+ 		hrtimer_setup(&x->mtimer, xfrm_timer_handler, CLOCK_BOOTTIME,
+ 			      HRTIMER_MODE_ABS_SOFT);
+ 		timer_setup(&x->rtimer, xfrm_replay_timer_handler, 0);
+@@ -754,6 +797,12 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
+ 		x->lft.hard_packet_limit = XFRM_INF;
+ 		x->replay_maxage = 0;
+ 		x->replay_maxdiff = 0;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		do {
++			x->handle = xfrm_state_handle++;
++		} while (x->handle == 0);
++		x->in_byh_hash = 0;
++#endif
+ 		x->pcpu_num = UINT_MAX;
+ 		spin_lock_init(&x->lock);
+ 		x->mode_data = NULL;
+@@ -829,6 +878,12 @@ int __xfrm_state_delete(struct xfrm_state *x)
+ 
+ 		if (x->id.spi)
+ 			hlist_del_rcu(&x->byspi);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		if (x->handle && x->in_byh_hash) {
++			hlist_del_rcu(&x->byh);
++			x->in_byh_hash = 0;
++		}
++#endif
+ 		net->xfrm.state_num--;
+ 		xfrm_nat_keepalive_state_updated(x);
+ 		spin_unlock(&net->xfrm.xfrm_state_lock);
+@@ -1582,6 +1637,13 @@ found:
+ 						  net->xfrm.state_byseq + h,
+ 						  x->xso.type);
+ 			}
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++			if (x->handle && !x->in_byh_hash) {
++				h = x->handle & net->xfrm.state_hmask;
++				hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h);
++				x->in_byh_hash = 1;
++			}
++#endif
+ 			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+ 			hrtimer_start(&x->mtimer,
+ 				      ktime_set(net->xfrm.sysctl_acq_expires, 0),
+@@ -1752,6 +1814,14 @@ static void __xfrm_state_insert(struct xfrm_state *x)
+ 				  x->xso.type);
+ 	}
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	if (x->handle && !x->in_byh_hash) {
++		h = x->handle & net->xfrm.state_hmask;
++		hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h);
++		x->in_byh_hash = 1;
++	}
++#endif
++
+ 	hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
+ 	if (x->replay_maxage)
+ 		mod_timer(&x->rtimer, jiffies + x->replay_maxage);
+@@ -1773,6 +1843,9 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
+ 	u32 mark = xnew->mark.v & xnew->mark.m;
+ 	u32 if_id = xnew->if_id;
+ 	u32 cpu_id = xnew->pcpu_num;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	u16 parent_sa_handle = 0;
++#endif
+ 
+ 	h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
+ 	hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
+@@ -1782,9 +1855,17 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
+ 		    x->pcpu_num		== cpu_id &&
+ 		    (mark & x->mark.m) == x->mark.v &&
+ 		    xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
+-		    xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family))
++		    xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family)) {
+ 			x->genid++;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++			if (!parent_sa_handle)
++				parent_sa_handle = x->handle;
++#endif
++		}
+ 	}
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	xnew->parent_sa_handle = parent_sa_handle;
++#endif
+ }
+ 
+ void xfrm_state_insert(struct xfrm_state *x)
+@@ -2352,6 +2433,37 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark,
+ }
+ EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
+ 
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++struct xfrm_state *__xfrm_state_lookup_byhandle(struct net *net, u16 handle)
++{
++	unsigned int h = handle & net->xfrm.state_hmask;
++	struct xfrm_state *x;
++
++	hlist_for_each_entry(x, net->xfrm.state_byh + h, byh) {
++		if (x->handle != handle)
++			continue;
++
++		xfrm_state_hold(x);
++		return x;
++	}
++
++	return NULL;
++}
++
++struct xfrm_state *
++xfrm_state_lookup_byhandle(struct net *net, u16 handle)
++{
++	struct xfrm_state *x;
++
++	spin_lock_bh(&net->xfrm.xfrm_state_lock);
++	x = __xfrm_state_lookup_byhandle(net, handle);
++	spin_unlock_bh(&net->xfrm.xfrm_state_lock);
++
++	return x;
++}
++EXPORT_SYMBOL(xfrm_state_lookup_byhandle);
++#endif
++
+ struct xfrm_state *
+ xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
+ 	      u32 if_id, u32 pcpu_num, u8 proto, const xfrm_address_t *daddr,
+@@ -2603,6 +2715,13 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
+ 			x->id.spi = newspi;
+ 			h = xfrm_spi_hash(net, &x->id.daddr, newspi, x->id.proto, x->props.family);
+ 			XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h, x->xso.type);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++			if (x->handle && !x->in_byh_hash) {
++				h = x->handle & net->xfrm.state_hmask;
++				hlist_add_head_rcu(&x->byh, net->xfrm.state_byh + h);
++				x->in_byh_hash = 1;
++			}
++#endif
+ 			spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ 			err = 0;
+ 			goto unlock;
+@@ -3279,6 +3398,12 @@ int __net_init xfrm_state_init(struct net *net)
+ 	net->xfrm.state_byseq = xfrm_hash_alloc(sz);
+ 	if (!net->xfrm.state_byseq)
+ 		goto out_byseq;
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	net->xfrm.state_byh = xfrm_hash_alloc(sz);
++	if (!net->xfrm.state_byh)
++		goto out_byh;
++	get_random_bytes(&xfrm_state_handle, sizeof(xfrm_state_handle));
++#endif
+ 
+ 	net->xfrm.state_cache_input = alloc_percpu(struct hlist_head);
+ 	if (!net->xfrm.state_cache_input)
+@@ -3294,6 +3419,10 @@ int __net_init xfrm_state_init(struct net *net)
+ 	return 0;
+ 
+ out_state_cache_input:
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	xfrm_hash_free(net->xfrm.state_byh, sz);
++out_byh:
++#endif
+ 	xfrm_hash_free(net->xfrm.state_byseq, sz);
+ out_byseq:
+ 	xfrm_hash_free(net->xfrm.state_byspi, sz);
+@@ -3321,9 +3450,15 @@ void xfrm_state_fini(struct net *net)
+ 		WARN_ON(!hlist_empty(net->xfrm.state_byspi + i));
+ 		WARN_ON(!hlist_empty(net->xfrm.state_bysrc + i));
+ 		WARN_ON(!hlist_empty(net->xfrm.state_bydst + i));
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++		WARN_ON(!hlist_empty(net->xfrm.state_byh + i));
++#endif
+ 	}
+ 
+ 	sz = (net->xfrm.state_hmask + 1) * sizeof(struct hlist_head);
++#if defined(CONFIG_INET_IPSEC_OFFLOAD) || defined(CONFIG_INET6_IPSEC_OFFLOAD)
++	xfrm_hash_free(net->xfrm.state_byh, sz);
++#endif
+ 	xfrm_hash_free(net->xfrm.state_byseq, sz);
+ 	xfrm_hash_free(net->xfrm.state_byspi, sz);
+ 	xfrm_hash_free(net->xfrm.state_bysrc, sz);
diff --git a/patches/split-kernel-patch.sh b/patches/split-kernel-patch.sh
new file mode 100644
index 0000000..142833e
--- /dev/null
+++ b/patches/split-kernel-patch.sh
@@ -0,0 +1,249 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# split-kernel-patch.sh
+# Split one big git-style patch into one patch file per touched file, then
+# optionally apply them one by one and stop at the first failure.
+#
+# Defaults match your ASK kernel patch workflow.
+#
+# Usage:
+#   ./split-kernel-patch.sh split
+#   ./split-kernel-patch.sh apply
+#   ./split-kernel-patch.sh check
+#   ./split-kernel-patch.sh reset-output
+#
+# Env overrides:
+#   PATCH_FILE=/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch
+#   LINUX_DIR=/src/linux
+#   OUT_DIR=/src/ASK/patches/kernel/split-002
+#   FUZZ=0
+#   USE_PATCH=0      # 0 = git apply, 1 = patch utility
+#   THREEWAY=0       # git apply --3way when USE_PATCH=0
+#   UPDATED_PATCH_DIR=/src/ASK/patches/kernel/updated-patch
+#                    # If a file with the same basename exists here, use it
+#                    # instead of the generated split fragment.
+
+PATCH_FILE="${PATCH_FILE:-/src/ASK/patches/kernel/002-mono-gateway-ask-kernel_linux_6_12.patch}"
+LINUX_DIR="${LINUX_DIR:-/src/linux}"
+OUT_DIR="${OUT_DIR:-/src/ASK/patches/kernel/split-002}"
+FUZZ="${FUZZ:-0}"
+USE_PATCH="${USE_PATCH:-0}"
+THREEWAY="${THREEWAY:-0}"
+UPDATED_PATCH_DIR="${UPDATED_PATCH_DIR:-$(dirname "$OUT_DIR")/updated-patch}"
+SERIES_FILE="${OUT_DIR}/series"
+LOG_FILE="${OUT_DIR}/apply.log"
+
+usage() {
+    sed -n '1,35p' "$0" >&2
+}
+
+need_file() {
+    [ -f "$1" ] || { echo "ERROR: missing file: $1" >&2; exit 1; }
+}
+
+need_dir() {
+    [ -d "$1" ] || { echo "ERROR: missing directory: $1" >&2; exit 1; }
+}
+
+resolve_patch_file() {
+    # Generated split fragments are immutable-ish.  During porting, put a
+    # corrected replacement patch in UPDATED_PATCH_DIR with the exact same
+    # basename, e.g.:
+    #   updated-patch/0005-drivers__net__ethernet__freescale__sdk_dpaa__dpaa_eth.h.patch
+    # The apply/check loop will use that replacement instead.
+    local generated="$1"
+    local replacement="${UPDATED_PATCH_DIR}/$(basename "$generated")"
+
+    if [ -f "$replacement" ]; then
+        printf '%s\n' "$replacement"
+    else
+        printf '%s\n' "$generated"
+    fi
+}
+
+patch_target_file() {
+    # Best-effort target display for one split fragment. Prefer the b/ path
+    # from the diff header, fall back to +++ if needed.
+    awk '
+        /^diff --git / {
+            p = $4
+            sub(/^b\//, "", p)
+            print p
+            exit
+        }
+        /^\+\+\+ / && $2 != "/dev/null" {
+            p = $2
+            sub(/^b\//, "", p)
+            print p
+            exit
+        }
+    ' "$1"
+}
+
+reset_output() {
+    rm -rf "$OUT_DIR"
+    mkdir -p "$OUT_DIR"
+}
+
+split_patch() {
+    need_file "$PATCH_FILE"
+    reset_output
+
+    # Lossless split: do NOT trim trailing whitespace and do NOT rewrite content.
+    # Each output starts at a 'diff --git ...' boundary.
+    awk -v outdir="$OUT_DIR" -v series="$SERIES_FILE" '
+        function sanitize(s) {
+            sub(/^b\//, "", s)
+            gsub(/^\"|\"$/, "", s)
+            gsub(/\//, "__", s)
+            gsub(/[^A-Za-z0-9._+-]/, "_", s)
+            if (length(s) > 160) s = substr(s, 1, 160)
+            return s
+        }
+        BEGIN {
+            n = 0
+            out = ""
+        }
+        /^diff --git / {
+            if (out != "") close(out)
+            n++
+            path = $4
+            safe = sanitize(path)
+            out = sprintf("%s/%04d-%s.patch", outdir, n, safe)
+            print out >> series
+        }
+        {
+            if (out != "") print $0 > out
+        }
+        END {
+            if (out != "") close(out)
+            if (n == 0) {
+                print "ERROR: no diff --git sections found" > "/dev/stderr"
+                exit 2
+            }
+            print n > outdir "/count"
+        }
+    ' "$PATCH_FILE"
+
+    echo "Split $(cat "$OUT_DIR/count") patch fragments into: $OUT_DIR"
+    echo "Series file: $SERIES_FILE"
+}
+
+ensure_split_exists() {
+    if [ ! -s "$SERIES_FILE" ]; then
+        echo "=> No split series found, splitting first..."
+        split_patch
+    fi
+}
+
+apply_one_git() {
+    patch_file="$1"
+
+    args=(--verbose)
+    if [ "$THREEWAY" = "1" ]; then
+        args+=(--3way)
+    fi
+
+    git apply "${args[@]}" --check "$patch_file"
+    git apply "${args[@]}" "$patch_file"
+}
+
+apply_one_patch_utility() {
+    patch_file="$1"
+
+    # --forward avoids reapplying already-applied hunks.
+    # --reject leaves .rej files for manual whack-a-mole.
+    patch -p1 --forward --batch --fuzz="$FUZZ" --dry-run < "$patch_file"
+    patch -p1 --forward --batch --fuzz="$FUZZ" --reject < "$patch_file"
+}
+
+check_or_apply() {
+    mode="$1"
+    ensure_split_exists
+    need_dir "$LINUX_DIR"
+
+    : > "$LOG_FILE"
+    cd "$LINUX_DIR"
+
+    i=0
+    total=$(wc -l < "$SERIES_FILE" | tr -d ' ')
+
+    while IFS= read -r patch_file; do
+        i=$((i + 1))
+        generated_patch_file="$patch_file"
+        patch_file=$(resolve_patch_file "$generated_patch_file")
+        base=$(basename "$generated_patch_file")
+        patching_target=$(patch_target_file "$patch_file")
+        if [ -z "$patching_target" ]; then
+            patching_target="unknown"
+        fi
+
+        if [ "$patch_file" != "$generated_patch_file" ]; then
+            echo "=> [$i/$total] $base (using updated-patch override)" | tee -a "$LOG_FILE"
+            echo "   override: $patch_file" >>"$LOG_FILE"
+        else
+            echo "=> [$i/$total] $base" | tee -a "$LOG_FILE"
+        fi
+
+        if [ "$mode" = "check" ]; then
+            if git apply --check "$patch_file" >>"$LOG_FILE" 2>&1; then
+                echo "   OK" | tee -a "$LOG_FILE"
+                continue
+            fi
+            echo "   FAIL: $patch_file" | tee -a "$LOG_FILE"
+            echo "Stopped at: $patch_file"
+            echo "Inspect log: $LOG_FILE"
+            echo "Target file: $patching_target"
+            echo ""
+            exit 1
+        fi
+
+        if [ "$USE_PATCH" = "1" ]; then
+            if apply_one_patch_utility "$patch_file" >>"$LOG_FILE" 2>&1; then
+                echo "   applied" | tee -a "$LOG_FILE"
+                continue
+            fi
+        else
+            if apply_one_git "$patch_file" >>"$LOG_FILE" 2>&1; then
+                echo "   applied" | tee -a "$LOG_FILE"
+                continue
+            fi
+        fi
+
+        echo "   FAILED: $patch_file" | tee -a "$LOG_FILE"
+        echo ""
+        echo "Stopped at: $patch_file"
+        echo "Inspect log: $LOG_FILE"
+        echo "Target file: $patching_target"
+        echo ""
+        echo "Useful next commands:"
+        echo "  cd $LINUX_DIR"
+        echo "  git diff"
+        echo "  git status --short"
+        echo "  ${USE_PATCH:+find . -name '*.rej' -o -name '*.orig'}"
+        exit 1
+    done < "$SERIES_FILE"
+
+    echo "All $total patch fragments ${mode}ed successfully."
+}
+
+cmd="${1:-split}"
+case "$cmd" in
+    split)
+        split_patch
+        ;;
+    check)
+        check_or_apply check
+        ;;
+    apply)
+        check_or_apply apply
+        ;;
+    reset-output)
+        reset_output
+        ;;
+    *)
+        usage
+        exit 2
+        ;;
+esac