* [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries()
@ 2026-10-19 13:22 Haoran Wang
2026-10-19 13:29 ` netdev-bot+sinfo
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Haoran Wang @ 2026-10-19 13:22 UTC (permalink / raw)
To: Varun Prakash
Cc: Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-rt-devel, linux-scsi, netdev, linux-kernel, haoranwangsec
ppm_get_cpu_entries() disables preemption with get_cpu(), reads the
current CPU's pool with per_cpu_ptr(), then acquires spin_lock_bh()
while preemption is still disabled, and finally put_cpu().
cpu = get_cpu();
pool = per_cpu_ptr(ppm->pool, cpu);
spin_lock_bh(&pool->lock);
put_cpu();
On PREEMPT_RT kernels spin_lock_bh() becomes a sleeping lock
(rt_spin_lock). get_cpu() raises preempt_count, so acquiring the
lock right after sleeps with preempt_count still positive. Sleeping
in that state is the scheduling-while-atomic case, which under
CONFIG_DEBUG_ATOMIC_SLEEP reports a BUG and, if the lock is contended,
deadlocks on the spot. The trigger path is any iSCSI offload I/O
through a Chelsio NIC that reaches ppm_get_cpu_entries().
The preemption disable exists only to stabilize the per-CPU accessor,
the pool is serialized by pool->lock, not by preemption. Replace
get_cpu() + per_cpu_ptr() + put_cpu() with raw_smp_processor_id() +
raw_cpu_ptr(), which read the current CPU without disabling preemption.
On non-RT kernels spin_lock_bh() does not sleep, so the BUG does not
fire there; the defect is PREEMPT_RT-specific.
With the fix, preempt_count is 0 at spin_lock_bh(), so rt_spin_lock()'s
might_sleep() no longer triggers.
Signed-off-by: Haoran Wang <haoranwangsec@gmail.com>
---
drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
index 2e3973a32..5f68c5d57 100644
--- a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
+++ b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
@@ -125,10 +125,9 @@ static int ppm_get_cpu_entries(struct cxgbi_ppm *ppm, unsigned int count,
if (!ppm->pool)
return -EINVAL;
- cpu = get_cpu();
- pool = per_cpu_ptr(ppm->pool, cpu);
+ cpu = raw_smp_processor_id();
+ pool = raw_cpu_ptr(ppm->pool);
spin_lock_bh(&pool->lock);
- put_cpu();
i = ppm_find_unused_entries(pool->bmap, ppm->pool_index_max,
pool->next, count, 0);
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries()
2026-10-19 13:22 [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries() Haoran Wang
@ 2026-10-19 13:29 ` netdev-bot+sinfo
2026-10-19 13:29 ` Sebastian Andrzej Siewior
2026-10-20 13:22 ` sashiko-bot
2 siblings, 0 replies; 4+ messages in thread
From: netdev-bot+sinfo @ 2026-10-19 13:29 UTC (permalink / raw)
To: Haoran Wang
Cc: Varun Prakash, Sebastian Andrzej Siewior, Clark Williams,
Steven Rostedt, linux-rt-devel, linux-scsi, netdev, linux-kernel
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
- What hardware the change was tested on. For driver fixes please
mention the device (and if relevant firmware version) used for
testing, or say that the change was not tested on real hardware.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries()
2026-10-19 13:22 [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries() Haoran Wang
2026-10-19 13:29 ` netdev-bot+sinfo
@ 2026-10-19 13:29 ` Sebastian Andrzej Siewior
2026-10-20 13:22 ` sashiko-bot
2 siblings, 0 replies; 4+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-10-19 13:29 UTC (permalink / raw)
To: Haoran Wang
Cc: Varun Prakash, Clark Williams, Steven Rostedt, linux-rt-devel,
linux-scsi, netdev, linux-kernel
On 2026-10-19 21:22:27 [+0800], Haoran Wang wrote:
> diff --git a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> index 2e3973a32..5f68c5d57 100644
> --- a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> +++ b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> @@ -125,10 +125,9 @@ static int ppm_get_cpu_entries(struct cxgbi_ppm *ppm, unsigned int count,
> if (!ppm->pool)
> return -EINVAL;
>
> - cpu = get_cpu();
> - pool = per_cpu_ptr(ppm->pool, cpu);
> + cpu = raw_smp_processor_id();
> + pool = raw_cpu_ptr(ppm->pool);
cpu needs too be obtained after the lock has been acquired.
Please also explain why it is okay to obtain pool belonging to CPU1 on
CPU0.
> spin_lock_bh(&pool->lock);
> - put_cpu();
>
> i = ppm_find_unused_entries(pool->bmap, ppm->pool_index_max,
> pool->next, count, 0);
Sebastian
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries()
2026-10-19 13:22 [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries() Haoran Wang
2026-10-19 13:29 ` netdev-bot+sinfo
2026-10-19 13:29 ` Sebastian Andrzej Siewior
@ 2026-10-20 13:22 ` sashiko-bot
2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-20 13:22 UTC (permalink / raw)
To: Haoran Wang; +Cc: linux-rt-devel, linux-scsi, linux-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] libcxgb: CPU migration between raw_smp_processor_id() and raw_cpu_ptr()
--
commit 45e692c0f47c6b069f27d74df45dc3a4a6360a3d
Author: Haoran Wang <haoranwangsec@gmail.com>
libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries()
This commit replaces get_cpu(), per_cpu_ptr(), and put_cpu() in
ppm_get_cpu_entries() with raw_smp_processor_id() and raw_cpu_ptr() to avoid
acquiring spin_lock_bh() with preemption disabled on PREEMPT_RT kernels.
> diff --git a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> index 2e3973a32d9d1..5f68c5d5766e5 100644
> --- a/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> +++ b/drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c
> @@ -125,10 +125,9 @@ static int ppm_get_cpu_entries(struct cxgbi_ppm *ppm, unsigned int count,
> if (!ppm->pool)
> return -EINVAL;
>
> - cpu = get_cpu();
> - pool = per_cpu_ptr(ppm->pool, cpu);
> + cpu = raw_smp_processor_id();
> + pool = raw_cpu_ptr(ppm->pool);
> spin_lock_bh(&pool->lock);
> - put_cpu();
>
> i = ppm_find_unused_entries(pool->bmap, ppm->pool_index_max,
> pool->next, count, 0);
[Severity: Critical]
Can the task be preempted and migrated to another CPU between
raw_smp_processor_id() and raw_cpu_ptr(ppm->pool)?
Because preemption is enabled here, if a task in a preemptible iSCSI offload
path (such as cxgbit_ddp_reserve() -> cxgbi_ppm_ppods_reserve() ->
ppm_get_cpu_entries()) is migrated from CPU A to CPU B between the two
calls, cpu will hold CPU A while pool will point to CPU B's per-CPU pool.
ppm_get_cpu_entries() will then allocate bits in CPU B's pool->bmap, but
compute the global pagepod index using CPU A's ID:
drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c:ppm_get_cpu_entries() {
...
i += cpu * ppm->pool_index_max;
ppm_mark_entries(ppm, i, count, caller_data);
return i;
}
Because those bits were never marked in CPU A's pool->bmap, a subsequent
allocation on CPU A can hand out the same global index while it is still
active, overwriting ppm->ppod_data[i] and corrupting in-flight iSCSI DDP
DMA transfers.
Later, when cxgbi_ppm_ppod_release() -> ppm_unmark_entries() releases the
entries, it derives the pool from the global index:
drivers/net/ethernet/chelsio/libcxgb/libcxgb_ppm.c:ppm_unmark_entries() {
...
if (i < ppm->pool_rsvd) {
unsigned int cpu;
struct cxgbi_ppm_pool *pool;
cpu = i / ppm->pool_index_max;
i %= ppm->pool_index_max;
pool = per_cpu_ptr(ppm->pool, cpu);
spin_lock_bh(&pool->lock);
bitmap_clear(pool->bmap, i, count);
...
}
This clears the bits in CPU A's pool->bmap while permanently leaking the
bits that were set in CPU B's pool->bmap.
Would keeping per_cpu_ptr(ppm->pool, cpu) after raw_smp_processor_id() avoid
this mismatch by ensuring cpu and pool always refer to the same per-CPU pool
even if migration occurs?
([
---
])
--
Sashiko AI review · https://sashiko.dev/#/patchset/179241614797.5916.13928836073297916808@whr?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-20 13:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 13:22 [PATCH] libcxgb: use raw_cpu_ptr() in ppm_get_cpu_entries() Haoran Wang
2026-10-19 13:29 ` netdev-bot+sinfo
2026-10-19 13:29 ` Sebastian Andrzej Siewior
2026-10-20 13:22 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox