|WARNING| [v3,9/9] net/mana: use common sysfs routines
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Tue Sep 15 02:53:56 CEST 2026
Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/169559
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-09-15
# DPDK Patch Review
## Patch 1/9: eal: add common sysfs value routines
### Errors
1. **Missing error checks in eal_sysfs.c (Error paths that skip cleanup)**
In `sysfs_read_line()` at line 48-50:
```c
if (fgets(buf, buflen, f) == NULL) {
EAL_LOG(ERR, "cannot read %s", path);
fclose(f);
return -1;
}
```
If `fgets()` fails, the file is closed. However, at line 52:
```c
fclose(f);
```
The file is closed again unconditionally. If the earlier `fgets()` branch is taken, this results in a **double-close** (calling `fclose()` on an already-closed `FILE*`).
**Fix:** Remove the `fclose()` from the error path on line 50, or restructure to use a single exit point with cleanup.
2. **Missing bounds check in `rte_sysfs_write_string()` (Buffer overflow risk)**
At lib/eal/linux/eal_sysfs.c:171-175:
```c
if (fputs(str, f) < 0) {
EAL_LOG(ERR, "cannot write '%s' to %s", str, path);
fclose(f);
return -1;
}
```
The `str` parameter is user-supplied and its length is not validated before being printed to the log via `%s`. If `str` is very large or contains format specifiers, this could cause issues. However, the more pressing concern is that `fputs()` itself does not validate that `str` is NUL-terminated or bounded -- if `str` is not NUL-terminated, this is undefined behavior.
**Recommendation:** Add a length check or document that `str` must be NUL-terminated. If the API guarantees `str` is NUL-terminated (as the function signature `const char *str` implies), then this is acceptable, but worth noting in the function documentation.
### Warnings
1. **Inconsistent error return semantics in EAL functions**
The new `rte_sysfs_parse_*()` functions return `-1` on all errors, losing the original `errno` information. The old `eal_parse_sysfs_value()` preserved `errno` implicitly since callers could check it after a `-1` return. Now, callers see only `-1` and cannot distinguish "file not found" from "permission denied" from "parse error."
The functions do log the error, but applications that want to handle different errors differently (e.g., retry on ENOENT vs. fail permanently on EACCES) cannot do so.
**Suggestion:** Consider returning `-errno` on error (as is common in DPDK APIs) or document that callers should check the log level and cannot recover based on error type.
2. **Test coverage gap for edge cases**
In `app/test/test_eal_fs.c`, the new tests add coverage for:
- Valid unsigned/signed values
- Negative values rejected by unsigned variant
- String read/write with newline stripping
- Empty file rejection
- Garbage value rejection
However, they do not test:
- Very large values (e.g., `ULONG_MAX` or values that overflow `uint32_t` when callers like `power_sysfs_read_u32()` are used)
- Files with only whitespace (e.g., `" \n"`)
- Files with multiple lines (does it read only the first line correctly?)
- Path truncation (what happens if the formatted path exceeds `PATH_MAX`?)
**Recommendation:** Add tests for these edge cases to ensure robustness.
3. **Silent truncation in `rte_sysfs_parse_string()`**
At lib/eal/linux/eal_sysfs.c:141:
```c
ret = sysfs_read_line(buf, buflen, format, ap);
```
If the sysfs value is longer than `buflen`, `fgets()` truncates it. The function returns success, and the caller gets a partial value with no indication that truncation occurred.
For some attributes (like long device paths or multi-value strings), this could be a logic error.
**Suggestion:** Document that the function truncates, or check if the buffer is full and return an error if the value did not fit.
4. **Inconsistent handling of trailing newline**
`sysfs_read_line()` at line 55:
```c
*strchrnul(buf, '\n') = '\0';
```
This strips the trailing newline unconditionally. The numeric parsers (`strtoul`, `strtol`) are indifferent to the newline since they stop at non-digits, so this is fine for them. However, if a future caller wanted to distinguish "value with newline" from "value without," they cannot.
This is not an error, but it is a behavior change from the old `eal_parse_sysfs_value()`, which left the newline in place and relied on `strtoul()` to stop parsing at it.
**Recommendation:** Document this behavior clearly in the function comments.
### Info
1. **Performance consideration**
Every call to these functions opens, reads, and closes a file. For attributes that are read frequently (e.g., in a polling loop), this is much slower than opening the file once and reading it repeatedly. The existing code in `drivers/power/intel_pstate` keeps `f_min` and `f_max` open for this reason.
The patch correctly leaves those hot-path files alone (as noted in the commit message), but future users should be aware that the new routines are for occasional reads, not for fast-path polling.
2. **Use of `strchrnul()` is Linux-specific**
At lib/eal/linux/eal_sysfs.c:55:
```c
*strchrnul(buf, '\n') = '\0';
```
`strchrnul()` is a GNU extension. The code is in `lib/eal/linux/`, so this is correct (as noted in the commit message). However, the commit message states "the declarations stay in `lib/eal/include` since they are part of the driver SDK headers," which might confuse readers into thinking the implementation is portable.
**Clarification:** The functions are declared in the public header but only *implemented* on Linux (via `#ifdef RTE_EXEC_ENV_LINUX` guards). The test is skipped on other platforms. This is correct but worth emphasizing in the documentation.
---
## Patch 2/9: dma/idxd: use common sysfs routines
### Errors
None.
### Warnings
1. **Type conversion in `dsa_probe_device()`**
At drivers/dma/idxd/idxd_bus.c:154-159:
```c
long val;
if (read_wq_int(dsa_dev, "size", &val) < 0)
return -1;
idxd.max_batches = val;
if (read_wq_int(dsa_dev, "max_batch_size", &val) < 0)
return -1;
idxd.max_batch_size = val;
```
`val` is `long` (signed), but `idxd.max_batches` and `idxd.max_batch_size` are likely unsigned types (not shown in the patch, but typical for size/count fields). If the sysfs file contained a negative value (which would be a kernel bug), this would assign a negative value to an unsigned field, resulting in a very large positive value.
**Recommendation:** Check that `val >= 0` before assigning, or use the unsigned read routine instead if these values are known to be non-negative.
---
## Patch 3/9: common/ionic: use common sysfs routines
### Errors
None.
### Warnings
1. **Stubs do not set errno or return a meaningful error code**
At drivers/common/ionic/ionic_common_uio.c:332-337 (the non-Linux stubs):
```c
RTE_EXPORT_INTERNAL_SYMBOL(ionic_uio_get_rsrc)
void
ionic_uio_get_rsrc(const char *name __rte_unused, int idx __rte_unused,
struct ionic_dev_bar *bar __rte_unused)
{
}
```
The function returns `void` and does nothing. If a caller (mistakenly) calls this on a non-Linux platform, they get no indication that it failed. The `bar` structure is left in an indeterminate state.
**Recommendation:** Set `bar->vaddr = NULL` and `bar->len = 0` to indicate failure, or change the return type to `int` and return `-ENOTSUP`.
---
## Patch 4/9: bus/vmbus: use common sysfs routines
### Errors
None.
### Warnings
1. **Error code change in `vmbus_uio_sysfs_read()`**
The commit message states:
> On an open failure the helper now returns -EIO rather than -errno; both callers only pass it to strerror(-err) in a log message, so this is message text only.
While this is true, it means that a caller who wanted to distinguish "file not found" from "permission denied" cannot do so anymore. If the driver ever adds logic to handle these errors differently (e.g., probe a fallback path if ENOENT), the information is lost.
**Recommendation:** Document this behavior change clearly, or consider preserving the distinction by using `rte_sysfs_parse_uint()` directly and checking `errno` before returning a generic error.
---
## Patch 5/9: power: use common sysfs routines
### Errors
1. **Uninitialized `FILE*` pointer removed but not consistently**
The commit message states:
> This also removes a few cases where the FILE pointer was left uninitialised and then tested against NULL in the cleanup path.
However, in `drivers/power/intel_pstate/intel_pstate_cpufreq.c:111-114`, the `f_min` and `f_max` pointers are still declared as:
```c
FILE *f_min = NULL, *f_max = NULL;
```
and the cleanup at lines 183-186 still tests them:
```c
err:
if (f_min != NULL)
fclose(f_min);
if (f_max != NULL)
fclose(f_max);
```
This is correct -- they are initialized to `NULL`. But the commit message implies that uninitialized-then-tested cases were removed, which is misleading.
**Clarification:** The removed cases were `f_base`, `f_base_min`, and `f_base_max`, which were left uninitialized in the original code and only conditionally assigned, leading to a potential test-against-uninitialized-value if the `open_core_sysfs_file()` calls failed. The patch fixes this by removing those variables entirely and using the new routines instead. The remaining `f_min` and `f_max` are still kept open for fast-path reads, and they are correctly initialized to `NULL`. The commit message is slightly unclear about this distinction.
### Warnings
1. **`power_sysfs_read_u32()` range check could be a warning instead of error**
At lib/power/power_common.c:85-87:
```c
if (tmp > UINT32_MAX) {
POWER_LOG(ERR, "sysfs value does not fit in 32 bits");
return -1;
}
```
If a sysfs file contains a value larger than `UINT32_MAX` (e.g., a 64-bit counter), the function fails and logs an error. For some attributes, this might be a kernel bug worth flagging; for others (like a large byte counter), it might just mean the caller should have used a 64-bit read.
**Recommendation:** Consider logging this as a warning and returning `UINT32_MAX` (clamped), or document that callers should use `rte_sysfs_parse_uint()` directly if they need 64-bit values.
---
## Patch 6/9: drivers/bus: remove duplicate sysfs string helpers
### Errors
None.
### Warnings
None.
---
## Patch 7/9: common/mlx5: use common sysfs routines
### Errors
None.
### Warnings
1. **Error code change in `mlx5_sys_roce_disable()`**
At drivers/common/mlx5/linux/mlx5_common_os.c:671-675:
```c
ret = rte_sysfs_write_string("0\n", MLX5_ROCE_ENABLE_PATH, addr);
if (ret != 0) {
rte_errno = ENOTSUP;
DRV_LOG(DEBUG, "Failed to disable ROCE by sysfs.");
return -ENOTSUP;
}
```
If the write fails for a reason other than "file not found" (e.g., EACCES, EROFS), the function returns `-ENOTSUP`, which is misleading. The old code returned `-errno`, preserving the actual error.
**Recommendation:** Return `-rte_errno` after setting `rte_errno = errno` (if the EAL routine preserved errno), or at least log the actual error before overwriting it with `ENOTSUP`.
---
## Patch 8/9: net/mlx5: use common sysfs routines
### Errors
None.
### Warnings
1. **Loss of error distinction in `mlx5_sysfs_bond_info()`**
At drivers/net/mlx5/linux/mlx5_ethdev_os.c:1203-1206:
```c
if (rte_sysfs_parse_uint(&val, "/sys/class/net/%s/master/ifindex", name) != 0) {
rte_errno = ENODEV;
return -rte_errno;
}
```
The old code set `rte_errno = errno` from `fopen()`, preserving whether the failure was ENOENT, EACCES, etc. Now, all failures become `ENODEV`, which is less informative.
**Recommendation:** Check `errno` after the call (if preserved) and set `rte_errno` accordingly, or document that all sysfs read failures are treated as "device not found."
---
## Patch 9/9: net/mana: use common sysfs routines
### Errors
None.
### Warnings
1. **Type mismatch in `get_port_mac()`**
At drivers/net/mana/mana.c:1286:
```c
unsigned long val;
```
then at line 1305:
```c
dev_port = val;
```
`dev_port` is `unsigned int`. On a 64-bit system, `val` is 64 bits, and truncating it to 32 bits could lose data if the kernel ever returns a value > `UINT_MAX`.
In practice, `dev_port` is a small integer (network interface index), so this is unlikely to overflow. However, for correctness, the code should either check `val <= UINT_MAX` or use a 32-bit variable from the start.
**Recommendation:** Add a range check before the assignment, or document that `dev_port` values are always < 2^32.
---
## Summary
**Errors** (must fix):
- Patch 1: Double-close in `sysfs_read_line()` error path
**Warnings** (should fix):
- Patch 1: Loss of errno information in error returns; test coverage gaps; string truncation not detected
- Patch 2: Signed-to-unsigned conversion without bounds check
- Patch 3: Non-Linux stubs do not indicate failure
- Patch 5: Commit message slightly misleading about uninitialized pointers; range check in `power_sysfs_read_u32
More information about the test-report
mailing list