|WARNING| [v2] eal/riscv: add support for zawrs extension

dpdklab at iol.unh.edu dpdklab at iol.unh.edu
Sun Nov 8 20:14:15 CET 2026


Test-Label: ai-code-review
Test-Status: WARNING
https://dpdk.org/patch/172732

_AI Code Review_

=== Patch Review: series.patch (via Claude) ===
Review date: 2026-11-08

# DPDK Patch Review

## ERRORS

**Error 1: Incorrect macro syntax in RTE_WAIT_UNTIL_MASKED**

Line 165: `if ((rte_atomic_load_explicit((addr), (memorder)) & (mask)) cond expected_value)`

The macro uses `cond` as a token-pasting parameter, but the `if` statement syntax is wrong.
The code writes `if (... cond expected_value)` where `cond` is expected to be an operator like `==` or `!=`,
but the syntax would produce `if (... == expected_value)` followed by `break;` outside any loop.
The `break;` at line 167 is not inside a loop context that the macro itself creates.

**Suggested fix:**
Wrap the entire macro body in a `do { ... } while (0)` loop so the `break;` at line 167 escapes the macro.
The current `do { ... } while (0)` only wraps the later loop, not the early-exit path.

---

**Error 2: Type mismatch in __RTE_RISCV_LR_16**

Line 75: `__RTE_RISCV_LR_32(((uintptr_t)(src) & (~3)), word, (memorder));`

`__RTE_RISCV_LR_32` expects its first argument to be a pointer (`"r" (src)` in the asm constraint),
but here it receives `(uintptr_t)(src) & (~3)`, which is an integer.
The assembly constraint `"r"` will compile, but this violates the design:
the `lr.w` instruction requires a register holding the address, not the address value itself.
The code happens to work because the integer is placed in a register,
but the pattern is wrong and fragile.

**Suggested fix:**
```c
#define __RTE_RISCV_LR_16(src, dst, memorder) do {                      \
	uint32_t word;                                                  \
	uint32_t *aligned_src = (uint32_t *)((uintptr_t)(src) & (~3UL)); \
	__RTE_RISCV_LR_32(aligned_src, word, (memorder));               \
	if ((uintptr_t)(src) & 2)                                       \
		(dst) = (typeof(dst))(word >> 16);                      \
	else                                                            \
		(dst) = (typeof(dst))(word & 0xFFFF);                   \
} while (0)
```

Note: the test `if ((size_t)(src) & 3)` at line 76 is also wrong --
it should be `& 2` to test the second bit (selecting upper vs lower halfword in the word).
The current code would also trigger on `& 1`, which cannot occur given the assertion,
but the intent is to distinguish addresses `...0` and `...2` within a word.

---

**Error 3: Macro __RTE_RISCV_LR missing `do { ... } while (0)`**

Line 82: `#define __RTE_RISCV_LR(src, dst, memorder, size) { ... }`

The macro body uses braces `{ }` instead of `do { ... } while (0)`.
If used in a context like `if (cond) __RTE_RISCV_LR(...); else ...`,
the semicolon after the macro call terminates the `if`, breaking the `else`.

**Suggested fix:**
```c
#define __RTE_RISCV_LR(src, dst, memorder, size) do {             \
	RTE_BUILD_BUG_ON(size != 16 && size != 32 && size != 64); \
	if (size == 16)                                           \
		__RTE_RISCV_LR_16(src, dst, memorder);            \
	else if (size == 32)                                      \
		__RTE_RISCV_LR_32(src, dst, memorder);            \
	else if (size == 64)                                      \
		__RTE_RISCV_LR_64(src, dst, memorder);            \
} while (0)
```

---

## WARNINGS

**Warning 1: Missing RTE_EXPORT_SYMBOL annotations**

The patch adds three new public functions: `rte_wait_until_equal_16`, `rte_wait_until_equal_32`, `rte_wait_until_equal_64`.
These are `static inline` in a header, so they do not require `RTE_EXPORT_*` macros.
However, the generic versions of these functions (in `lib/eal/include/generic/rte_pause.h`)
are also inline and are not annotated.
This is acceptable for inline-only API, but the pattern is worth noting.

(No action required -- inline functions in headers do not need export annotations.)

---

**Warning 2: Memory order validation incomplete**

Lines 95-96 (and similar in the 32-bit and 64-bit variants):
```c
RTE_ASSERT(memorder == rte_memory_order_acquire ||
	memorder == rte_memory_order_relaxed);
```

The assertion only fires in debug builds.
In production, if a caller passes `rte_memory_order_release` or `rte_memory_order_seq_cst`,
the code proceeds with the `else` branch in `__RTE_RISCV_LR_*`, using the `.aq` (acquire) variant,
which happens to work for seq_cst but is wrong for release.

This is not a bug (release memorder makes no sense for a load),
but the code silently accepts invalid input in non-debug builds.

**Suggested improvement:**
Use `RTE_BUILD_BUG_ON` if memorder can be constant, or add an `else return;` fallback:
```c
if (memorder != rte_memory_order_acquire &&
    memorder != rte_memory_order_relaxed)
	return;  /* invalid memorder, do nothing */
```

---

**Warning 3: Inconsistent assertion check in RTE_WAIT_UNTIL_MASKED**

Line 158: `RTE_ASSERT(((size_t)(addr) & (sizeof(*(addr)) - 1)) == 0);`

The runtime assertion checks natural alignment, but the macro already has a compile-time check
at line 157 for memorder validity.
The alignment check should also be compile-time when `addr` is a constant expression,
but `RTE_ASSERT` is runtime-only.

**Suggested improvement:**
This is acceptable as-is (alignment is usually runtime-determined),
but consider documenting that unaligned `addr` is UB even in non-debug builds
(the `lr.*` instruction will trap).

---

## INFO

**Info 1: Release notes describe build-time feature, not runtime detection**

The release notes (line 75-76 in the patch) say:
```
On RISC-V targets built with the Zawrs extension in the ISA string
(for example ``-Dcpu_instruction_set=rv64gcv_zawrs``),
```

This is accurate. The feature is build-time only (`#ifdef RTE_RISCV_FEATURE_ZAWRS`),
not runtime-detected.
If a binary is built without Zawrs and run on a Zawrs-capable CPU, it will not use `wrs.nto`.

This is acceptable for RISC-V where ISA features are typically baked in at compile time,
but worth noting.

---

**Info 2: Signed vs unsigned integer in __RTE_RISCV_LR_16 shift**

Line 77: `(dst) = (typeof(dst))(word >> 16);`

If `dst` is a signed `int16_t`, the cast happens after the shift.
`word` is `uint32_t`, so the shift is unsigned (no sign extension issue here),
but the pattern could be clearer by using an unsigned intermediate explicitly.

This is not a bug -- the code is correct as written.

---

**Info 3: Documentation of wrs.nto behavior**

The comment at line 63-66 says:
```
* Wait-on-Reservation-Set extension instruction, it stalls execution until the
* reservation set is invalidated or an interrupt is observed.
* A loop is likely still needed as it may stop stalling arbitrarily.
```

The Zawrs specification permits `wrs.nto` to resume spuriously even without an interrupt
or reservation invalidation (for example, on a timer or implementation-defined event).
The comment correctly notes this ("may stop stalling arbitrarily"),
and the code correctly loops after every wake.

No issue -- this is good documentation.

---

Review-Result: ERROR


More information about the test-report mailing list