|WARNING| [v2,4/4] doc: update 26.11 release notes for cnxk dmadev
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Sun Sep 20 17:04:48 CEST 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/169811
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-09-20
# DPDK Patch Review
## Summary
This patch series adds O20 (CN20K) DPI DMA hardware support to the cnxk driver.
The series consists of 4 patches:
1. Common/cnxk: ROC support for CN20K DPI
2. Dma/cnxk: DMA driver support for CN20K
3. Dma/cnxk: Inter-process domain DMA support
4. Doc: Release notes update
---
## Patch 1/4: common/cnxk: add O20 DPI DMA support
### Errors
**Resource leak on error path in `roc_dpi_rsrc_init()`**
In `roc_dpi_rsrc_init()` (roc_dpi.c:680):
- `plt_memzone_reserve_cache_align()` allocates `mz`
- `dpi_lf_attach()` may fail after `mz` is stored in `roc_dpi->mz`
- On failure, the function frees `mz` and sets pointers to NULL
- **However**, if `dpi_lf_attach()` succeeds but `dpi_lf_init()` loop fails partway through, the function returns the error without cleanup
- The memzone and any partially initialized LFs are leaked
```c
rc = dpi_lf_attach(dev, blk_addr, true, roc_dpi->nr_lfs);
if (rc) {
plt_err("Could not attach LFs");
plt_memzone_free(mz);
roc_dpi->mz = NULL;
roc_dpi->lfs = NULL;
return rc; /* Good: cleaned up */
}
for (slot = 0; slot < roc_dpi->nr_lfs; slot++)
dpi_lf_init(&(roc_dpi->lfs[slot]), dev, slot);
return rc; /* BAD: if dpi_lf_init failed (returns non-zero), mz is leaked */
```
**Fix**: Check `rc` after the `dpi_lf_init()` loop and clean up on error:
```c
for (slot = 0; slot < roc_dpi->nr_lfs; slot++) {
rc = dpi_lf_init(&(roc_dpi->lfs[slot]), dev, slot);
if (rc) {
dpi_lf_detach(dev);
plt_memzone_free(mz);
roc_dpi->mz = NULL;
roc_dpi->lfs = NULL;
return rc;
}
}
```
---
**Missing error check on `roc_dpi_lf_chan_tbl_alloc()` in `cn20k_dmadev_queue_setup()`**
In `cn20k_dmadev_queue_setup()` (cnxk_dmadev.c:568-576 in patch 2/4, but originates from ROC code added in patch 1):
```c
rc = roc_dpi_lf_ring_init(que, &cfg);
if (rc)
return rc; /* checked */
/* ... assignment to dpi_conf ... */
```
Later in the calling function `cnxk_dmadev_start()`:
```c
rc = cn20k_dmadev_queue_setup(dpivf);
if (rc)
goto error;
rc = cn20k_dmadev_chan_tbl_setup(dpivf);
/* chan_tbl_setup depends on chan_tbl_alloc having succeeded */
```
The `roc_dpi_lf_chan_tbl_alloc()` call in the configure path stores the channel table in `lf->chan_tbl`.
If it fails, `lf->chan_tbl` is uninitialized, but the code proceeds to use it in `cn20k_dmadev_chan_tbl_setup()`.
**Fix**: `roc_dpi_lf_chan_tbl_alloc()` returns negative on error; verify and propagate it:
```c
rc = roc_dpi_lf_chan_tbl_alloc(lf, dpivf->num_vchans);
if (rc < 0) {
plt_err("Failed to allocate chan tbl");
return rc; /* instead of storing rc and ignoring */
}
```
---
**Incorrect mempool size calculation potentially exceeds hardware limit**
In `roc_dpi.c`, `roc_dpi_configure_v2()`:
```c
#define DPI_POOL_SZ(d) ((1ULL << (d)) / 8) /* d is block_size */
/* ... */
queue_buf_sz = CNXK_DPI_QUEUE_BUF_SIZE_V2; /* 130944 from patch 2 */
block_size = rte_log2_u32(queue_buf_sz);
if (!rte_is_power_of_2(queue_buf_sz))
block_size += 1;
nb_chunks = CNXK_DPI_CHUNKS_FROM_DESC(queue_buf_sz, nb_desc);
/* ... */
pool_sz = DPI_POOL_SZ(block_size); /* 2^block_size / 8 */
```
The maximum pool size supported by the hardware is `128 * 1024` (per comments in patch 2).
With `CNXK_DPI_QUEUE_BUF_SIZE_V2 = 130944`:
- `block_size = rte_log2_u32(130944) + 1 = 17 + 1 = 18`
- `pool_sz = (1ULL << 18) / 8 = 262144 / 8 = 32768` pool entries
- Each entry is `queue_buf_sz` bytes, so total pool memory = `32768 * 130944 = 4,292,870,144` bytes (~4GB)
**This exceeds the 128KB device limit** and will fail when the mempool is created.
Patch 2 changes `CNXK_DPI_QUEUE_BUF_SIZE_V2` from `130944` to `130048` and adds a comment explaining the 127KB limit.
However, the calculation here uses the old larger value, and the mempool allocation does not verify the computed size against the hardware maximum before calling `rte_mempool_create()`.
**Fix**:
1. Verify that `queue_buf_sz` does not exceed `DPI_MAX_POOL_SIZE` (127KB).
2. Fail early with a clear error if the requested buffer size would exceed the hardware limit.
3. Ensure the mempool size respects the per-object and total pool size constraints.
---
**Incorrect symbol export name in `roc_platform_base_symbols.c`**
```c
RTE_EXPORT_INTERNAL_SYMBOL(roc_dpi_access_pair_group_handler_get)
```
The function is named `roc_dpi_access_pair_group_handler_get()`, but the English word is "handle" not "handler".
The function returns a "handle" (an identifier), not a "handler" (a callback or service function).
**Fix**: Rename to:
```c
int roc_dpi_access_pair_group_handle_get(...);
RTE_EXPORT_INTERNAL_SYMBOL(roc_dpi_access_pair_group_handle_get)
```
And update the function name in `roc_dpi.h` and `roc_dpi.c` to match.
---
### Warnings
**Hardcoded memzone name length in `dpi_lf_queue_configure()`**
```c
char nm[ROC_DPI_DEV_NAME_LEN] = {'\0'};
/* ... */
snprintf(nm, sizeof(nm), "%s_%u_%u_%x", "dpi_lf_q", lf->slot, rcfg->ring_idx,
lf->dev->pf_func);
```
`ROC_DPI_DEV_NAME_LEN` is defined as `sizeof(ROC_DPI_DEV_NAME) + PCI_PRI_STR_SIZE`.
The format string includes a prefix, two `%u` (up to 5 digits each), one `%x` (up to 4 hex digits), and underscores.
If `slot`, `ring_idx`, or `pf_func` are large, the string could exceed the buffer.
**Suggestion**: Use a larger buffer or verify that `ROC_DPI_DEV_NAME_LEN` accounts for the maximum possible string length.
---
**Inconsistent devargs parsing error handling**
In `cn20k_dmadev_parse_devargs()` (patch 2):
```c
kvlist = rte_kvargs_parse(devargs->args, NULL);
if (kvlist == NULL)
goto exit;
/* ... */
exit:
rte_kvargs_free(kvlist);
return -EINVAL;
```
If `rte_kvargs_parse()` returns NULL, the code jumps to `exit` which calls `rte_kvargs_free(NULL)`.
While `rte_kvargs_free()` tolerates NULL, the pattern is inconsistent with typical DPDK error paths
where a NULL allocation is handled immediately without cleanup.
**Suggestion**: Return directly on NULL `kvlist`:
```c
if (kvlist == NULL)
return -EINVAL;
```
---
**Shadowed variable name `rc` in nested scope**
In `cnxk_dmadev_configure()` (patch 2), the function declares `int rc = 0;` at the top,
then in a CN20K-specific block:
```c
if (roc_model_is_cn20k()) {
rc = cnxk_dmadev_vchan_rsrc_free(dpivf);
if (rc < 0)
goto error;
/* ... more uses of rc ... */
}
```
The `rc` variable is reused for multiple calls, which is acceptable but can obscure errors
if an early failure is overwritten by a later success.
**Suggestion**: Initialize `rc` to a sentinel value (e.g., `-1`) and verify that all paths set it explicitly.
---
## Patch 2/4: dma/cnxk: add O20 DPI DMA support
### Errors
**Use-after-free potential in `cnxk_dmadev_vchan_rsrc_free()`**
```c
if (rdpi->lfs == NULL)
return 0;
rc = roc_dpi_lf_chan_tbl_free(&(rdpi->lfs[0]));
/* ... */
rc = roc_dpi_rsrc_fini(rdpi);
/* roc_dpi_rsrc_fini frees rdpi->mz, which contains rdpi->lfs */
dpivf->is_ring_conf_done = false;
return rc;
```
After `roc_dpi_rsrc_fini()` frees `rdpi->mz`, the pointer `rdpi->lfs` becomes stale but is not set to NULL.
A subsequent call to `cnxk_dmadev_vchan_rsrc_free()` will pass the stale pointer check `if (rdpi->lfs == NULL)`
and attempt to dereference `rdpi->lfs[0]`, causing a use-after-free.
**Fix**: Set `rdpi->lfs = NULL` after `roc_dpi_rsrc_fini()`:
```c
rc = roc_dpi_rsrc_fini(rdpi);
if (rc < 0)
plt_err("Failed to free dpi lfs");
/* roc_dpi_rsrc_fini already sets rdpi->mz and rdpi->lfs to NULL on success,
but ensure it's done even on failure */
rdpi->lfs = NULL;
```
Verify that `roc_dpi_rsrc_fini()` in patch 1 sets both pointers to NULL on all exit paths.
---
**Double-free of `dpivf->ring_conf` in error path**
In `cnxk_dmadev_configure()`:
```c
if (roc_model_is_cn20k()) {
rc = cnxk_dmadev_vchan_rsrc_free(dpivf);
if (rc < 0)
goto error;
/* ... */
dpivf->ring_conf = rte_zmalloc(...);
if (dpivf->ring_conf == NULL) {
rc = -ENOMEM;
goto error;
}
/* ... more allocations that can fail ... */
}
error:
if (roc_model_is_cn20k() && rc < 0)
cnxk_dmadev_vchan_rsrc_free(dpivf);
```
If the `rte_zmalloc()` for `ring_conf` succeeds, but a later allocation fails (e.g., `roc_dpi_rsrc_init()`),
the `goto error` calls `cnxk_dmadev_vchan_rsrc_free()` again, which frees `ring_conf` a second time:
```c
if (dpivf->ring_conf) {
rte_free(dpivf->ring_conf);
dpivf->ring_conf = NULL;
}
```
This is a double-free because `ring_conf` was already freed in the first call to `cnxk_dmadev_vchan_rsrc_free()`.
**Fix**: Set `dpivf->ring_conf = NULL` immediately after freeing:
```c
if (dpivf->ring_conf) {
rte_free(dpivf->ring_conf);
dpivf->ring_conf = NULL;
}
```
Alternatively, check whether the allocation that failed was *after* `ring_conf` was set,
and only call cleanup if needed.
---
**Missing `rc` initialization in `cnxk_dmadev_start()`**
```c
if (roc_model_is_cn20k()) {
for (i = 0; i < (dpivf->rdpi.nr_lfs << 1); i++)
dpivf->ring_conf[i].pending = 0;
rc = roc_dpi_reset(&dpivf->rdpi);
if (rc < 0) {
plt_err("Failed to reset DPI LFs");
goto error;
}
/* ... */
goto enable_dpi;
}
/* Non-CN20K path */
queue_buf_sz = CNXK_DPI_QUEUE_BUF_SIZE_V2;
/* ... */
enable_dpi:
roc_dpi_enable(&dpivf->rdpi);
error:
return rc;
```
If the CN20K path is taken and succeeds, `rc` is set to `0` by `roc_dpi_reset()`.
However, if the non-CN20K path is taken, `rc` is only set if an error occurs.
If the non-CN20K setup succeeds, `rc` is undefined at the `enable_dpi` label,
and the function returns garbage.
**Fix**: Initialize `rc = 0;` at the top of `cnxk_dmadev_start()`.
---
**Variable assigned then overwritten before read in `cn20k_dmadev_parse_devargs()`**
```c
uint16_t num_lfs = num_vchans >> 1;
/* ... */
if (rte_kvargs_process(kvlist, CN20K_DPI_NUM_LFS, &parse_val_u16, &num_lfs) < 0)
goto exit;
```
The initial assignment `num_lfs = num_vchans >> 1` is overwritten by `rte_kvargs_process()` if the key is present.
If the key is absent, `parse_val_u16()` is not called and `num_lfs` retains its default.
This is correct behavior (default value if key absent, parsed value if present),
but the initial assignment is **always overwritten** if the key exists, making it a dead store.
**Not an error**, just an observation. The code is correct as-is.
---
### Warnings
**Potential memzone name collision with concurrent calls**
In `dpi_lf_queue_configure()`:
```c
snprintf(nm, sizeof
More information about the test-report
mailing list