Got dpa_app working

This commit is contained in:
2026-05-10 22:25:46 +08:00
parent 8bc9737032
commit 9ccd41bc54
9 changed files with 571 additions and 23 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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;
}

View File

@@ -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