penguin/monok8s

k8s image for Mono Gateway Dev Kit

commit 9ccd41bc542748ecdfbd239af847b0e1ea4ad1f1225dab3e759987156129af92

author斟酌 鵬兄 <tgckpg@gmail.com>
date2026-05-10T14:25:46Z
subjectGot dpa_app working
commit 9ccd41bc542748ecdfbd239af847b0e1ea4ad1f1225dab3e759987156129af92
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2026-05-10T14:25:46Z

    Got dpa_app working
---
 alpine/install-packages.sh                         |   4 +-
 docker/ask.Dockerfile                              |   8 +-
 docs/vendor-resources.md                           |  68 +++++++
 kernel-build/dts/mono-gateway-dk-sdk.dts           |  31 ++--
 kernel-build/dts/mono-gateway-dk.dts               |   4 +-
 ...a-app-allow-xml-config-path-env-overrides.patch |   2 +-
 .../ask/dpa/0002-cdx-harden-fman-port-lookup.patch | 152 +++++++++++++++
 ...oid-kfree-of-userspace-dist-info-pointers.patch | 120 ++++++++++++
 ...-cdx-stash-userspace-fman-nested-pointers.patch | 205 +++++++++++++++++++++
 9 files changed, 571 insertions(+), 23 deletions(-)

diff --git a/alpine/install-packages.sh b/alpine/install-packages.sh
index d5679c3..d924634 100755
--- a/alpine/install-packages.sh
+++ b/alpine/install-packages.sh
@@ -9,9 +9,9 @@ apk add alpine-base \
 
 # For diagnotics
 apk add \
-  iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq binutils \
+  iproute2 iproute2-ss curl bind-tools procps strace tcpdump lsof jq gdb binutils \
   openssl conntrack-tools ethtool findmnt kmod coreutils util-linux zstd libcap-utils \
-  iotop sysstat
+  iotop sysstat dtc
 echo '[ -x /bin/bash ] && exec /bin/bash -l' >> "/root/.profile"
 
 # Compat layer for kubelet for now. Will look into building it myself later. If needed
diff --git a/docker/ask.Dockerfile b/docker/ask.Dockerfile
index 4e42dd8..5302e61 100644
--- a/docker/ask.Dockerfile
+++ b/docker/ask.Dockerfile
@@ -248,7 +248,7 @@ RUN make -C "${ASK_DIR}/cmm" \
 		PKG_CONFIG_PATH="${SYSROOT}/lib/pkgconfig" && \
 	make -C "${ASK_DIR}/dpa_app" \
 		CC="aarch64-linux-musl-gcc -static" \
-		CFLAGS="-DDPAA_DEBUG_ENABLE -DNCSW_LINUX \
+		CFLAGS="-DDPAA_DEBUG_ENABLE -DLS1043 -DNCSW_LINUX -D__STDC_LIMIT_MACROS \
 			-I/src/fmc/source \
 			-I${ASK_DIR}/cdx \
 			-I/src/fmlib/include/fmd \
@@ -265,8 +265,8 @@ RUN mkdir -p /out/ASK/dist && \
 	mkdir -p /out/ASK/bin && \
 	cp /src/fmc/source/fmc /out/ASK/bin/ && \
 	cp "${ASK_DIR}/cmm/src/cmm" /out/ASK/bin/ && \
-	cp "${ASK_DIR}/dpa_app/dpa_app" /out/ASK/bin/ && \
-	aarch64-linux-musl-strip /out/ASK/bin/fmc /out/ASK/bin/cmm /out/ASK/bin/dpa_app
+	cp "${ASK_DIR}/dpa_app/dpa_app" /out/ASK/bin/
+#	aarch64-linux-musl-strip /out/ASK/bin/fmc /out/ASK/bin/cmm /out/ASK/bin/dpa_app
 
 # in-tree Linux kernel modules
 RUN mkdir -p /out/rootfs && \
@@ -282,7 +282,7 @@ RUN KERNEL_VER=$(ls /out/rootfs/lib/modules/) && \
 RUN mkdir -p /out/rootfs-cfg/etc/dpa && \
 	cp -r "${ASK_DIR}/dpa_app/files/etc/"* /out/rootfs-cfg/etc/dpa/ && \
 	cp "${ASK_DIR}/config/gateway-dk/cdx_cfg.xml" /out/rootfs-cfg/etc/dpa/ && \
-	cp -r /src/fmc/etc /out/rootfs-cfg/etc
+	cp -r /src/fmc/etc/* /out/rootfs-cfg/etc
 
 FROM scratch AS export
 
diff --git a/docs/vendor-resources.md b/docs/vendor-resources.md
index 41d6ee1..01bda09 100644
--- a/docs/vendor-resources.md
+++ b/docs/vendor-resources.md
@@ -1,3 +1,6 @@
+## ASK
+The most important one is vendor's ASK. Which provides all the required resources to build the device-specific kernel
+
 ## Updating build.env
 You can find the latest package versions in here
  * [kernel](https://github.com/nxp-qoriq/linux/archive/refs/tags/)
@@ -10,3 +13,68 @@ You can find the latest package versions in here
 https://github.com/we-are-mono/OpenWRT-ASK/tree/mono-25.12.0-rc3/target/linux/layerscape/files/arch/arm64/boot/dts/freescale
 * We need both `mono-gateway-dk-sdk.dts` and `mono-gateway-dk.dts` since the sdk one includes the non-sdk one.
 * The actual dts being used is the `mono-gateway-dk-sdk.dts`
+
+## Testing dpa_app
+Run this on the gateway device
+```bash
+while true; do nc -l -p 1234 -e sh; done
+```
+
+Run this script on your dev machine
+```bash
+#!/bin/bash
+FILES="
+bin/dpa_app
+rootfs-cfg/etc/dpa/cdx_cfg.xml
+rootfs-cfg/etc/dpa/cdx_pcd.xml
+rootfs-cfg/etc/dpa/cdx_sp.xml
+rootfs-cfg/etc/dpa/cdx_cfg_ls1046_rdb.xml
+rootfs-cfg/etc/fmc/config/hxs_pdl_v3.xml
+rootfs-cfg/etc/fmc/config/cfgdata.xsd
+rootfs-cfg/etc/fmc/config/netpcd.xsd
+"
+
+SIZE=$(
+  tar -C ./out/ASK -czf - $FILES | wc -c
+)
+
+(
+  echo 'set -eu'
+
+  echo 'rm -rf /var/dpa-test'
+  echo 'mkdir -p /var/dpa-test/bin'
+  echo 'mkdir -p /etc/dpa'
+  echo 'mkdir -p /etc/fmc/config'
+
+  echo 'base64 -d > /tmp/dpa-test.tar.gz <<'"'"'EOF'"'"''
+  tar -C ./out/ASK -czf - $FILES | pv -s "$SIZE" | base64
+  echo 'EOF'
+
+  echo 'tar -xzf /tmp/dpa-test.tar.gz -C /var/dpa-test'
+
+  echo 'cp /var/dpa-test/bin/dpa_app /var/dpa_app'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_cfg.xml /etc/dpa/cdx_cfg.xml'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_pcd.xml /etc/dpa/cdx_pcd.xml'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/dpa/cdx_sp.xml /etc/dpa/cdx_sp.xml'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/hxs_pdl_v3.xml /etc/fmc/config/hxs_pdl_v3.xml'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/cfgdata.xsd /etc/fmc/config/cfgdata.xsd'
+  echo 'cp /var/dpa-test/rootfs-cfg/etc/fmc/config/netpcd.xsd /etc/fmc/config/netpcd.xsd'
+
+  echo 'chmod +x /var/dpa_app'
+
+  echo 'export CDX_CFG_FILE=/etc/dpa/cdx_cfg.xml'
+  echo 'export CDX_PCD_FILE=/etc/dpa/cdx_pcd.xml'
+  echo 'export CDX_SP_FILE=/etc/dpa/cdx_sp.xml'
+  echo 'export CDX_PDL_FILE=/etc/fmc/config/hxs_pdl_v3.xml'
+
+  echo 'echo "CDX_CFG_FILE=$CDX_CFG_FILE"'
+  echo 'echo "CDX_PCD_FILE=$CDX_PCD_FILE"'
+  echo 'echo "CDX_SP_FILE=$CDX_SP_FILE"'
+  echo 'echo "CDX_PDL_FILE=$CDX_PDL_FILE"'
+
+  echo 'ls -l /var/dpa_app /etc/dpa /etc/fmc/config'
+  echo 'echo Running /var/dpa_app'
+  echo '/var/dpa_app'
+  echo 'echo exit=$?'
+) | nc 10.0.0.10 1234
+```
diff --git a/kernel-build/dts/mono-gateway-dk-sdk.dts b/kernel-build/dts/mono-gateway-dk-sdk.dts
index 8ea2c5b..0c1ac11 100644
--- a/kernel-build/dts/mono-gateway-dk-sdk.dts
+++ b/kernel-build/dts/mono-gateway-dk-sdk.dts
@@ -263,22 +263,23 @@
 	 * ethernet@8 = enet6 = MAC9 (10G)
 	 */
 
-    ethernet@8 {
-        compatible = "fsl,dpa-ethernet-init";
-        fsl,bman-buffer-pools = <&bp7 &bp8 &bp9>;
-        fsl,qman-frame-queues-rx = <0x5c 1 0x5d 1>;
-        fsl,qman-frame-queues-tx = <0x7c 1 0x7d 1>;
-    };
+	ethernet@8 {
+		compatible = "fsl,dpa-ethernet";
+		/delete-property/ fsl,bman-buffer-pools;
+		/delete-property/ fsl,qman-frame-queues-rx;
+		/delete-property/ fsl,qman-frame-queues-tx;
+		dma-coherent;
+	};
 
 	/* Add MAC10 - not in qoriq-dpaa-eth.dtsi */
-    ethernet@9 {
-        compatible = "fsl,dpa-ethernet-init";
-        fsl,fman-mac = <&enet7>;
-        fsl,bman-buffer-pools = <&bp7 &bp8 &bp9>;
-        fsl,qman-frame-queues-rx = <0x5e 1 0x5f 1>;
-        fsl,qman-frame-queues-tx = <0x7e 1 0x7f 1>;
-        dma-coherent;
-    };
+	ethernet@9 {
+		compatible = "fsl,dpa-ethernet";
+		fsl,fman-mac = <&enet7>;
+		/delete-property/ fsl,bman-buffer-pools;
+		/delete-property/ fsl,qman-frame-queues-rx;
+		/delete-property/ fsl,qman-frame-queues-tx;
+		dma-coherent;
+	};
 };
 
 &fman0 {
@@ -336,12 +337,14 @@
 	};
 
 	ethernet@f0000 {
+		status = "okay";
 		/delete-property/ managed;
 		fixed-link = <0 1 10000 0 0>;
 		phy-connection-type = "xgmii";
 	};
 
 	ethernet@f2000 {
+		status = "okay";
 		/delete-property/ managed;
 		fixed-link = <0 1 10000 0 0>;
 		phy-connection-type = "xgmii";
diff --git a/kernel-build/dts/mono-gateway-dk.dts b/kernel-build/dts/mono-gateway-dk.dts
index d2d7aea..df7c1ae 100644
--- a/kernel-build/dts/mono-gateway-dk.dts
+++ b/kernel-build/dts/mono-gateway-dk.dts
@@ -500,14 +500,14 @@
 		status = "okay";
 	};
 
-	fm1_mac9: ethernet@f0000 { /* 10GEC1 */
+	fm0_mac9: ethernet@f0000 { /* 10GEC1 */
 		sfp = <&sfp_xfi0>;
 		phy-connection-type = "10gbase-r";
 		managed = "in-band-status";
 		pcs-handle-names = "xfi";  /* Match enet7 for consistency */
 	};
 
-	fm1_mac10: ethernet@f2000 { /* 10GEC2 */
+	fm0_mac10: ethernet@f2000 { /* 10GEC2 */
 		sfp = <&sfp_xfi1>;
 		phy-connection-type = "10gbase-r";
 		managed = "in-band-status";
diff --git a/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch b/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch
index 4ae1766..786f022 100644
--- a/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch
+++ b/patches/ask/dpa/0001-dpa-app-allow-xml-config-path-env-overrides.patch
@@ -1,7 +1,7 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: monok8s <monok8s@example.invalid>
 Date: Sun, 10 May 2026 00:00:00 +0000
-Subject: [PATCH] dpa_app: allow XML config paths to be overridden by env
+Subject: [PATCH 1/4] dpa_app: allow XML config paths to be overridden by env
 
 Keep the vendor default XML paths, but allow deployments to override them
 without patching the binary or placing board-specific XML files directly
diff --git a/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch b/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch
new file mode 100644
index 0000000..9cede6a
--- /dev/null
+++ b/patches/ask/dpa/0002-cdx-harden-fman-port-lookup.patch
@@ -0,0 +1,152 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: monok8s <monok8s@example.invalid>
+Date: Sun, 10 May 2026 00:00:00 +0000
+Subject: [PATCH 2/4] cdx: harden FMAN port lookup
+
+The DPA userspace loader passes FMC-derived port metadata into the CDX
+ioctl. If an Ethernet netdev has a partially initialized DPAA private
+structure, find_osdev_by_fman_params() can dereference a missing mac_dev
+or FM wrapper pointer while trying to match the FMC port.
+
+Add lookup diagnostics and hard NULL guards so a mismatched board
+model returns -ENODEV instead of oopsing the kernel.
+---
+ cdx/devman.c  | 85 +++++++++++++++++++++++++++++++++++++--------------
+ cdx/dpa_cfg.c | 18 ++++++++---
+ 2 files changed, 76 insertions(+), 27 deletions(-)
+
+diff -urN a/cdx/devman.c b/cdx/devman.c
+--- a/cdx/devman.c	2026-05-10 00:32:28.745375897 +0000
++++ b/cdx/devman.c	2026-05-10 00:32:29.834300280 +0000
+@@ -396,39 +396,74 @@
+ 		uint32_t speed)
+ {
+ 	struct net_device *device;
+-	struct dpa_priv_s *priv;	
+-	struct mac_device *macdev;
++
++	DPA_INFO("%s::lookup fm %u port %u speed %uG\n",
++			__func__, fm_idx, port_idx, speed);
+ 
+ 	device = first_net_device(&init_net);
+-	while(1) {
+-		if (!device) 
+-			break;
+-		if (device->type == ARPHRD_ETHER) {
+-			t_LnxWrpFmDev *p_LnxWrpFmDev;
+-			priv = netdev_priv(device);
+-			macdev = priv->mac_dev;
+-			if (macdev) {
+-				p_LnxWrpFmDev = (t_LnxWrpFmDev*)macdev->fm;
+-				if (speed == 10) {
+-					//10 gig interfaces upports only SUPPORTED_10000baseT_Full
+-					/*DGW board has 2 fixed-link interfaces 
+-						1 - (eth2)(xDSL)1G Fixed link interface linked to rgmii-txid
+-						2 - eth5(G.fast)- 1G Fixed link interface linked to sgmii and
+-						connected to 10G link of the board.
+-						sgmii - considered as 1000baseT_Full and this has cell_index = 0*/
+-
+-					if ( (!macdev->fixed_link) && (macdev->if_support != SUPPORTED_10000baseT_Full) )
+-						goto next_device; 
+-				}
+-				if ((fm_idx == p_LnxWrpFmDev->id) && 
+-						(port_idx == macdev->cell_index))
+-					return device;
+-			}
++	while (device) {
++		struct dpa_priv_s *priv;
++		struct mac_device *macdev;
++		t_LnxWrpFmDev *p_LnxWrpFmDev;
++
++		if (device->type != ARPHRD_ETHER)
++			goto next_device;
++
++		priv = netdev_priv(device);
++		if (!priv) {
++			DPA_INFO("%s::skip %s: null private data\n",
++					__func__, device->name);
++			goto next_device;
++		}
++
++		macdev = priv->mac_dev;
++		if (!macdev) {
++			DPA_INFO("%s::skip %s: null mac_dev\n",
++					__func__, device->name);
++			goto next_device;
++		}
++
++		if (!macdev->fm) {
++			DPA_INFO("%s::skip %s: null mac_dev->fm cell_index %u max_speed %u fixed_link %u if_support 0x%x\n",
++					__func__, device->name, macdev->cell_index,
++					macdev->max_speed, macdev->fixed_link,
++					macdev->if_support);
++			goto next_device;
+ 		}
++
++		p_LnxWrpFmDev = (t_LnxWrpFmDev *)macdev->fm;
++		DPA_INFO("%s::candidate %s fm %u cell_index %u max_speed %u fixed_link %u if_support 0x%x\n",
++				__func__, device->name, p_LnxWrpFmDev->id,
++				macdev->cell_index, macdev->max_speed,
++				macdev->fixed_link, macdev->if_support);
++
++		if (speed == 10) {
++			//10 gig interfaces upports only SUPPORTED_10000baseT_Full
++			/*DGW board has 2 fixed-link interfaces
++				1 - (eth2)(xDSL)1G Fixed link interface linked to rgmii-txid
++				2 - eth5(G.fast)- 1G Fixed link interface linked to sgmii and
++				connected to 10G link of the board.
++				sgmii - considered as 1000baseT_Full and this has cell_index = 0*/
++
++			if ((!macdev->fixed_link) &&
++					(macdev->if_support != SUPPORTED_10000baseT_Full))
++				goto next_device;
++		}
++
++		if ((fm_idx == p_LnxWrpFmDev->id) &&
++				(port_idx == macdev->cell_index)) {
++			DPA_INFO("%s::matched %s for fm %u port %u speed %uG\n",
++					__func__, device->name, fm_idx, port_idx, speed);
++			return device;
++		}
++
+ next_device:
+ 		device = next_net_device(device);
+ 	}
+-	return device;
++
++	DPA_ERROR("%s::no OS device found for fm %u port %u speed %uG\n",
++			__func__, fm_idx, port_idx, speed);
++	return NULL;
+ }
+ 
+ 
+diff -urN a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c
+--- a/cdx/dpa_cfg.c	2026-05-10 00:32:28.757992164 +0000
++++ b/cdx/dpa_cfg.c	2026-05-10 00:32:51.954850425 +0000
+@@ -301,15 +301,21 @@
+ 		struct net_device *dev;
+ 
+ 		if (port_info->type) {
++			DPA_INFO("%s::mapping user port %s fm %u index %u portid %u type %uG\n",
++					__func__, port_info->name, port_info->fm_index,
++					port_info->index, port_info->portid, port_info->type);
+ 			dev = find_osdev_by_fman_params(port_info->fm_index,
+ 					port_info->index, port_info->type);
+ 			if (!dev) {
+-				DPA_ERROR("%s::could not map port %s\n",
+-						__func__, port_info->name);
+-				return -EIO;
+-			} else {
+-				strcpy(port_info->name, dev->name);
++				DPA_ERROR("%s::could not map port %s fm %u index %u portid %u type %uG\n",
++						__func__, port_info->name,
++						port_info->fm_index, port_info->index,
++						port_info->portid, port_info->type);
++				return -ENODEV;
+ 			}
++			DPA_INFO("%s::mapped user port %s to netdev %s\n",
++					__func__, port_info->name, dev->name);
++			strscpy(port_info->name, dev->name, sizeof(port_info->name));
+ 		}
+ #ifdef DPA_CFG_DEBUG
+ 		DPA_INFO("%s::port %s, fmindex %d, port index %d, port id %d\n",
diff --git a/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch b/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch
new file mode 100644
index 0000000..c6b0f5d
--- /dev/null
+++ b/patches/ask/dpa/0003-cdx-avoid-kfree-of-userspace-dist-info-pointers.patch
@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: monok8s <monok8s@example.invalid>
+Date: Sun, 10 May 2026 00:00:00 +0000
+Subject: [PATCH 3/4] cdx: avoid kfree of userspace dist_info pointers
+
+get_port_info() copies an array of cdx_port_info from userspace. At that
+point each cdx_port_info.dist_info field is still a userspace pointer.
+However release_cfg_info() treats any non-NULL dist_info as a kernel
+allocation and kfree()s it on error paths.
+
+If a later step fails before get_dist_info() has replaced every dist_info
+with a kernel allocation, release_cfg_info() can kfree a raw userspace
+pointer and oops in kfree()/virt_to_folio().
+
+Stash the userspace dist_info pointers in a temporary array, clear the
+kernel-side cdx_port_info.dist_info fields immediately after copy_from_user(),
+and pass the saved userspace pointer explicitly to get_dist_info(). This
+keeps release_cfg_info() safe on partial-initialization failures.
+---
+ cdx/dpa_cfg.c | 47 +++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 37 insertions(+), 10 deletions(-)
+
+diff -urN a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c
+--- a/cdx/dpa_cfg.c	2026-05-10 00:46:34.295813594 +0000
++++ b/cdx/dpa_cfg.c	2026-05-10 00:46:35.558487337 +0000
+@@ -169,11 +169,10 @@
+ }
+ 
+ //allocate and copy distribution info from uspace 
+-static int get_dist_info(struct cdx_port_info *port_info)
++static int get_dist_info(struct cdx_port_info *port_info, void *uspace_info)
+ {
+ 	uint32_t mem_size;
+ 	struct cdx_dist_info *dist_info;
+-	void *uspace_info;
+ 
+ #ifdef DPA_CFG_DEBUG
+ 	DPA_INFO("%s::port %s dist %d\n", __func__, 
+@@ -187,7 +186,6 @@
+ 		return -ENOMEM;
+ 	}
+ 	memset(dist_info, 0, mem_size);
+-	uspace_info = port_info->dist_info;
+ 	port_info->dist_info = dist_info;
+ 	if (copy_from_user(dist_info, uspace_info, 
+ 				mem_size)) {
+@@ -273,6 +271,7 @@
+ {	
+ 	struct cdx_port_info *port_info;
+ 	void *uspace_info;
++	void **uspace_dist_info;
+ 	uint32_t mem_size;
+ 	uint32_t ii;
+ 
+@@ -289,13 +288,40 @@
+ 		return -ENOMEM;
+ 	}
+ 	memset(port_info, 0, mem_size);
++
++	uspace_dist_info = kcalloc(finfo->max_ports, sizeof(*uspace_dist_info),
++			GFP_KERNEL);
++	if (!uspace_dist_info) {
++		DPA_ERROR("%s::memalloc for uspace_dist_info failed\n",
++				__func__);
++		kfree(port_info);
++		return -ENOMEM;
++	}
++
+ 	uspace_info = finfo->portinfo;
+ 	finfo->portinfo = port_info;
+ 	if (copy_from_user(port_info, uspace_info, mem_size)) {
+ 		DPA_ERROR("%s::Read port_info failed\n",
+ 				__func__);
++		finfo->portinfo = NULL;
++		kfree(uspace_dist_info);
++		kfree(port_info);
+ 		return -EIO;
+ 	}
++
++	/*
++	 * port_info has just been copied from userspace, so each dist_info
++	 * member is still a userspace pointer. release_cfg_info() kfree()s
++	 * non-NULL dist_info members, therefore keeping those raw userspace
++	 * pointers in the kernel copy turns any later error path into an
++	 * invalid kfree(). Stash the userspace pointers separately and clear
++	 * the struct fields until get_dist_info() replaces them with real
++	 * kernel allocations.
++	 */
++	for (ii = 0; ii < finfo->max_ports; ii++) {
++		uspace_dist_info[ii] = port_info[ii].dist_info;
++		port_info[ii].dist_info = NULL;
++	}
+ 	//put the linux name for the port
+ 	for (ii = 0; ii < finfo->max_ports; ii++) {
+ 		struct net_device *dev;
+@@ -311,6 +337,7 @@
+ 						__func__, port_info->name,
+ 						port_info->fm_index, port_info->index,
+ 						port_info->portid, port_info->type);
++				kfree(uspace_dist_info);
+ 				return -ENODEV;
+ 			}
+ 			DPA_INFO("%s::mapped user port %s to netdev %s\n",
+@@ -330,11 +357,14 @@
+ 	for (ii = 0; ii < finfo->max_ports; ii++) {
+ 		int retval;
+ 		//get dist info for this port
+-		retval = get_dist_info(port_info);
+-		if (retval)
++		retval = get_dist_info(port_info, uspace_dist_info[ii]);
++		if (retval) {
++			kfree(uspace_dist_info);
+ 			return retval;
++		}
+ 		port_info++;
+ 	}
++	kfree(uspace_dist_info);
+ 	return 0;
+ }	
+ 
diff --git a/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch b/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch
new file mode 100644
index 0000000..2344959
--- /dev/null
+++ b/patches/ask/dpa/0004-cdx-stash-userspace-fman-nested-pointers.patch
@@ -0,0 +1,205 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: monok8s <monok8s@example.invalid>
+Date: Sun, 10 May 2026 00:00:00 +0000
+Subject: [PATCH 4/4] cdx: stash userspace fman nested pointers before cleanup
+
+cdx_ioc_set_dpa_params() copies struct cdx_fman_info from userspace.
+The nested portinfo and tbl_info members are userspace pointers at that
+point, but release_cfg_info() treats non-NULL nested pointers as
+kernel-owned allocations.
+
+If setup fails before get_port_info() or get_cctbl_info() replaces those
+members with kernel allocations, the error path can kfree a userspace
+address and panic in kfree()/virt_to_folio().
+
+Stash the userspace pointers in temporary arrays, clear the nested
+members in fman_info immediately, and pass the stashed pointers into the
+copy helpers explicitly. Also route early setup failures through the same
+cleanup path so partial state is released consistently.
+---
+diff --git a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c
+index c678d5d..55b910d 100644
+--- a/cdx/dpa_cfg.c
++++ b/cdx/dpa_cfg.c
+@@ -267,10 +267,9 @@ static void *get_dist_info_by_fman_params(struct cdx_fman_info *finfo, uint32_t
+ #endif //CDX_RTP_RELAY
+ 
+ //allocate and copy port releated info from uspace 
+-static int get_port_info(struct cdx_fman_info *finfo) 
++static int get_port_info(struct cdx_fman_info *finfo, void *uspace_info)
+ {	
+ 	struct cdx_port_info *port_info;
+-	void *uspace_info;
+ 	void **uspace_dist_info;
+ 	uint32_t mem_size;
+ 	uint32_t ii;
+@@ -298,7 +297,6 @@ static int get_port_info(struct cdx_fman_info *finfo)
+ 		return -ENOMEM;
+ 	}
+ 
+-	uspace_info = finfo->portinfo;
+ 	finfo->portinfo = port_info;
+ 	if (copy_from_user(port_info, uspace_info, mem_size)) {
+ 		DPA_ERROR("%s::Read port_info failed\n",
+@@ -355,7 +353,7 @@ static int get_port_info(struct cdx_fman_info *finfo)
+ 	}
+ 	port_info = finfo->portinfo;
+ 	for (ii = 0; ii < finfo->max_ports; ii++) {
+-		int retval;
++		int retval = 0;
+ 		//get dist info for this port
+ 		retval = get_dist_info(port_info, uspace_dist_info[ii]);
+ 		if (retval) {
+@@ -369,11 +367,10 @@ static int get_port_info(struct cdx_fman_info *finfo)
+ }	
+ 
+ //allocate and copy cc table infor from uspace
+-static int get_cctbl_info(struct cdx_fman_info *finfo) 
++static int get_cctbl_info(struct cdx_fman_info *finfo, void *uspace_info)
+ {	
+ 	struct table_info *tbl_info;
+ 	uint32_t mem_size;
+-	void *uspace_info;
+ 
+ 	//allocate table information area
+ 	mem_size = (sizeof(struct table_info) * finfo->num_tables);
+@@ -384,7 +381,6 @@ static int get_cctbl_info(struct cdx_fman_info *finfo)
+ 		return -ENOMEM;
+ 	}
+ 	memset(tbl_info, 0, mem_size);
+-	uspace_info = finfo->tbl_info;
+ 	finfo->tbl_info = tbl_info;
+ 	//copy table related info from user space	
+ 	if (copy_from_user(tbl_info, (void *)uspace_info, mem_size)) {
+@@ -625,9 +621,11 @@ int cdx_ioc_set_dpa_params(unsigned long args)
+ {
+ 	struct cdx_ctrl_set_dpa_params params;
+ 	struct cdx_fman_info *finfo;
++	void **uspace_port_info = NULL;
++	void **uspace_tbl_info = NULL;
+ 	uint32_t ii;	
+ 	uint32_t mem_size;
+-	int retval;
++	int retval = 0;
+ 
+ 	if (copy_from_user(&params, (void *)args, 
+ 				sizeof(struct cdx_ctrl_set_dpa_params))) {
+@@ -655,6 +653,35 @@ int cdx_ioc_set_dpa_params(unsigned long args)
+ 		retval = -EIO;
+ 		goto err_ret;
+ 	}
++	uspace_port_info = kcalloc(num_fmans, sizeof(*uspace_port_info),
++			GFP_KERNEL);
++	uspace_tbl_info = kcalloc(num_fmans, sizeof(*uspace_tbl_info),
++			GFP_KERNEL);
++	if (!uspace_port_info || !uspace_tbl_info) {
++		DPA_ERROR("%s::unable to allocate user pointer stash\n",
++				__func__);
++		for (ii = 0; ii < num_fmans; ii++) {
++			fman_info[ii].portinfo = NULL;
++			fman_info[ii].tbl_info = NULL;
++		}
++		retval = -ENOMEM;
++		goto err_ret;
++	}
++
++	/*
++	 * fman_info is copied from userspace.  Its nested portinfo and
++	 * tbl_info members are userspace pointers until get_port_info() and
++	 * get_cctbl_info() replace them with kernel allocations.  Never leave
++	 * raw userspace pointers in fman_info, because release_cfg_info() owns
++	 * and frees non-NULL nested pointers on error paths.
++	 */
++	for (ii = 0; ii < num_fmans; ii++) {
++		uspace_port_info[ii] = fman_info[ii].portinfo;
++		uspace_tbl_info[ii] = fman_info[ii].tbl_info;
++		fman_info[ii].portinfo = NULL;
++		fman_info[ii].tbl_info = NULL;
++	}
++
+ 	if (copy_from_user(&ipr_info, (void *)params.ipr_info,
+ 				sizeof(struct cdx_ipr_info))) {
+ 		DPA_ERROR("%s::Read iprv_info failed\n", 
+@@ -665,22 +688,26 @@ int cdx_ioc_set_dpa_params(unsigned long args)
+ 	//init the fman handles 
+ 	finfo = fman_info;
+ 	for (ii = 0; ii < num_fmans; ii++) {
+-		if (cdxdrv_get_fman_handles(finfo))
+-			return -1;
++		if (cdxdrv_get_fman_handles(finfo)) {
++			retval = -EIO;
++			goto err_ret;
++		}
+ 		finfo++;
+ 	}
+ 	finfo = fman_info;
+ 	//init interface stats module
+-	if (cdxdrv_init_stats(finfo->muram_handle))
+-		return -1;
++	if (cdxdrv_init_stats(finfo->muram_handle)) {
++		retval = -EIO;
++		goto err_ret;
++	}
+ 
+ 	for (ii = 0; ii < num_fmans; ii++) {
+ 		//get port info
+-		retval = get_port_info(finfo);
++		retval = get_port_info(finfo, uspace_port_info[ii]);
+ 		if (retval)
+ 			goto err_ret;
+ 		//get cc table info
+-		retval = get_cctbl_info(finfo);
++		retval = get_cctbl_info(finfo, uspace_tbl_info[ii]);
+ 		if (retval)
+ 			goto err_ret;
+ 		finfo++;
+@@ -727,29 +754,43 @@ int cdx_ioc_set_dpa_params(unsigned long args)
+ 		finfo++;
+ 	}
+ 
+-	if (cdx_create_port_fqs())
+-		return -1;
++	if (cdx_create_port_fqs()) {
++		retval = -EIO;
++		goto err_ret;
++	}
+ 	//create cp rate limit policier profiles
+ 	if (cdxdrv_create_missaction_policer_profiles(fman_info)) {
++		retval = -EIO;
+ 		goto err_ret;
+ 	}
+ #ifdef ENABLE_INGRESS_QOS
+ 	if (cdxdrv_create_ingress_qos_policer_profiles(fman_info)) {
++		retval = -EIO;
+ 		goto err_ret;
+ 	}
+ #endif
+ #ifdef ENABLE_EGRESS_QOS
+-	if(ceetm_init_cq_plcr())
++	if(ceetm_init_cq_plcr()) {
++		retval = -EIO;
+ 		goto err_ret;
++	}
+ #endif
+ 	//init the fman and its ports
+ 	for (ii = 0; ii < num_fmans; ii++) {
+-		if (cdxdrv_set_miss_action(ii))
++		if (cdxdrv_set_miss_action(ii)) {
++			retval = -EIO;
+ 			goto err_ret;
++		}
+ 	}
+ 	display_dpa_cfg();
++	kfree(uspace_port_info);
++	kfree(uspace_tbl_info);
+ 	return 0;
+ err_ret:
++	DPA_ERROR("%s::error path retval %d, releasing partial DPA cfg\n",
++			__func__, retval);
++	kfree(uspace_port_info);
++	kfree(uspace_tbl_info);
+ 	release_cfg_info();
+ 	return retval;
+ }
+-- 
+2.39.5