| 0/0 |
2026/08/25 00:56 |
flow |
assessment-security |
44m
Results: map[DenialOfService:true Explanation:The bug is a memory leak in the NVMe over Fabrics (NVMe-oF) RDMA target driver
(`nvmet-rdma`).
When a new RDMA CM connection request is received for an admin queue (`host_qid
== 0`), `nvmet_rdma_queue_connect()` allocates a new queue and its associated
RDMA resources using `nvmet_rdma_alloc_queue()`.
Inside `nvmet_rdma_alloc_queue()`, after allocating the Queue Pair (QP) and
Completion Queue (CQ), the code checks if there are too many pending controller
teardowns (`pending > NVMET_RDMA_BACKLOG`). If this limit is exceeded, the
function jumps to the `put_device` label to return an error:
```c
if (pending > NVMET_RDMA_BACKLOG) {
ret = NVME_SC_CONNECT_CTRL_BUSY;
goto put_device;
}
```
However, this jump skips the `free_queue` label, completely leaking the newly
allocated `queue` structure and all its RDMA resources (QP, CQ, command buffers,
etc.).
Because the CQ was allocated from a shared device pool using `ib_cq_pool_get()`,
its `cqe_used` counter was incremented. Since the queue is leaked,
`ib_cq_pool_put()` is never called to decrement the counter. Later, when the
RDMA device is unregistered (e.g., device removal or module unload),
`ib_cq_pool_cleanup()` verifies that all shared CQs are unused
(`WARN_ON(cq->cqe_used)`). The leaked CQ triggers this warning.
**Security Impact:**
* **Denial of Service (DoS):** An attacker can repeatedly trigger this error
path to leak kernel memory and RDMA resources, eventually leading to an
Out-Of-Memory (OOM) state or resource exhaustion.
* **Remote / Network Triggerable:** The vulnerability can be triggered remotely
by any unprivileged attacker who can reach the NVMe-oF target's RDMA port (e.g.,
over RoCE, iWARP, or InfiniBand) by sending a burst of connect and disconnect
requests to exceed the `NVMET_RDMA_BACKLOG` limit (which is 128).
* **Exploitability:** This is purely a memory leak. It does not result in memory
corruption (like use-after-free or out-of-bounds access) or privilege
escalation, so it is not exploitable beyond DoS. Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/25 00:56 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/25 00:56 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/08cdb2054d2b7486c8f9f6900f5a9dbb7eda350e]
|
| 3/1 |
2026/08/25 00:58 |
action |
kernel-builder |
20m
Results: map[KernelObj:/app/workdir/cache/build/2e5d9e021914f3f5384aae382776f2072102743f]
|
| 4/1 |
2026/08/25 01:18 |
action |
codesearch-prepare |
11m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/25 01:29 |
agent |
expert |
11m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
smbdirect: ib_dev[syz1] removed
------------[ cut here ]------------
cq->cqe_used
WARNING: drivers/infiniband/core/cq.c:370 at ib_cq_pool_cleanup+0x1b9/0x5c0 drivers/infiniband/core/cq.c:370, CPU#0: kworker/u8:17/9582
Modules linked in:
CPU: 0 UID: 0 PID: 9582 Comm: kworker/u8:17 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
Workqueue: ib-unreg-wq ib_unregister_work
RIP: 0010:ib_cq_pool_cleanup+0x1b9/0x5c0 drivers/infiniband/core/cq.c:370
Code: 34 24 4c 8b 7c 24 08 4d 39 f7 74 58 e8 a0 ee 85 f9 e9 c3 fe ff ff e8 96 ee 85 f9 41 80 3c 1e 00 75 8e eb 94 e8 88 ee 85 f9 90 <0f> 0b 90 e9 f6 fe ff ff 44 89 e1 80 e1 07 80 c1 03 38 c1 0f 8c cb
RSP: 0018:ffffc90004c6f948 EFLAGS: 00010293
RAX: ffffffff883ffd48 RBX: dffffc0000000000 RCX: ffff888033091f40
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000804 R08: 0000000000000000 R09: 0000000000000000
R10: dffffc0000000000 R11: fffffbfff1e640e4 R12: ffff88802b48a02c
R13: dead000000000100 R14: 1ffff11005691408 R15: ffff88802b48a040
FS: 0000000000000000(0000) GS:ffff888125beb000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055eb287ee950 CR3: 00000000678c6000 CR4: 00000000003526f0
Call Trace:
<TASK>
disable_device+0x15d/0x330 drivers/infiniband/core/device.c:1328
__ib_unregister_device+0x2c9/0x400 drivers/infiniband/core/device.c:1571
ib_unregister_work+0x19/0x30 drivers/infiniband/core/device.c:1683
process_one_work kernel/workqueue.c:3387 [inline]
process_scheduled_works+0xc3d/0x1630 kernel/workqueue.c:3470
worker_thread+0xa47/0xfb0 kernel/workqueue.c:3551
kthread+0x38b/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Reply: The bug is a memory leak in the NVMe over Fabrics (NVMe-oF) RDMA target driver (`nvmet-rdma`).
When a new RDMA CM connection request is received for an admin queue (`host_qid == 0`), `nvmet_rdma_queue_connect()` allocates a new queue and its associated RDMA resources using `nvmet_rdma_alloc_queue()`.
Inside `nvmet_rdma_alloc_queue()`, after allocating the Queue Pair (QP) and Completion Queue (CQ), the code checks if there are too many pending controller teardowns (`pending > NVMET_RDMA_BACKLOG`). If this limit is exceeded, the function jumps to the `put_device` label to return an error:
```c
if (pending > NVMET_RDMA_BACKLOG) {
ret = NVME_SC_CONNECT_CTRL_BUSY;
goto put_device;
}
```
However, this jump skips the `free_queue` label, completely leaking the newly allocated `queue` structure and all its RDMA resources (QP, CQ, command buffers, etc.).
Because the CQ was allocated from a shared device pool using `ib_cq_pool_get()`, its `cqe_used` counter was incremented. Since the queue is leaked, `ib_cq_pool_put()` is never called to decrement the counter. Later, when the RDMA device is unregistered (e.g., device removal or module unload), `ib_cq_pool_cleanup()` verifies that all shared CQs are unused (`WARN_ON(cq->cqe_used)`). The leaked CQ triggers this warning.
**Security Impact:**
* **Denial of Service (DoS):** An attacker can repeatedly trigger this error path to leak kernel memory and RDMA resources, eventually leading to an Out-Of-Memory (OOM) state or resource exhaustion.
* **Remote / Network Triggerable:** The vulnerability can be triggered remotely by any unprivileged attacker who can reach the NVMe-oF target's RDMA port (e.g., over RoCE, iWARP, or InfiniBand) by sending a burst of connect and disconnect requests to exceed the `NVMET_RDMA_BACKLOG` limit (which is 128).
* **Exploitability:** This is purely a memory leak. It does not result in memory corruption (like use-after-free or out-of-bounds access) or privilege escalation, so it is not exploitable beyond DoS.
|
| 6/2 |
2026/08/25 01:29 |
llm |
expert |
0m
Model:
Tokens:
input: 6964
output: 40
thoughts: 86
Thoughts: Here's my summary, as I see it:
**Analyzing an IB CQ Cleanup Warning**
Okay, so I've got a kernel bug report flashing a warning related to Infiniband, specifically in `drivers/infiniband/core/cq.c`. The warning is pointing to line 370 and highlighting `cq->cqe_used`. This suggests a potential issue within the completion queue (CQ) cleanup process. My immediate reaction is to dive into `drivers/infiniband/core/cq.c` and zero in around that line to understand what `ib_cq_pool_cleanup` is doing and, more importantly, *why* `cqe_used` is causing a warning there. I need to understand the lifecycle of the CQEs and how they're being managed during cleanup to understand the root cause of this condition. I'll be looking for potential race conditions, incorrect accounting of used entries, or possibly a double-free scenario, or even a logical error in the cleanup logic itself. My aim is to quickly assess what's going on with that used count and see what it's telling us about the health of the CQ.
|
| 7/2 |
2026/08/25 01:30 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/core/cq.c FirstLine:350 LineCount:50]
Results: map[Contents: 350: rdma_dim_destroy(cq);
351: trace_cq_free(cq);
352: if (cq->device->ops.post_destroy_cq)
353: cq->device->ops.post_destroy_cq(cq);
354: else
355: ret = cq->device->ops.destroy_cq(cq, NULL);
356: WARN_ONCE(ret, "Destroy of kernel CQ shouldn't fail");
357: kfree(cq->wc);
358: kfree(cq);
359: }
360: EXPORT_SYMBOL(ib_free_cq);
361:
362: void ib_cq_pool_cleanup(struct ib_device *dev)
363: {
364: struct ib_cq *cq, *n;
365: unsigned int i;
366:
367: for (i = 0; i < ARRAY_SIZE(dev->cq_pools); i++) {
368: list_for_each_entry_safe(cq, n, &dev->cq_pools[i],
369: pool_entry) {
370: WARN_ON(cq->cqe_used);
371: list_del(&cq->pool_entry);
372: cq->shared = false;
373: ib_free_cq(cq);
374: }
375: }
376: }
377:
378: static int ib_alloc_cqs(struct ib_device *dev, unsigned int nr_cqes,
379: enum ib_poll_context poll_ctx)
380: {
381: LIST_HEAD(tmp_list);
382: unsigned int nr_cqs, i;
383: struct ib_cq *cq, *n;
384: int ret;
385:
386: if (poll_ctx > IB_POLL_LAST_POOL_TYPE) {
387: WARN_ON_ONCE(poll_ctx > IB_POLL_LAST_POOL_TYPE);
388: return -EINVAL;
389: }
390:
391: /*
392: * Allocate at least as many CQEs as requested, and otherwise
393: * a reasonable batch size so that we can share CQs between
394: * multiple users instead of allocating a larger number of CQs.
395: */
396: nr_cqes = min(dev->attrs.max_cqe, max(nr_cqes, IB_MAX_SHARED_CQ_SZ));
397: nr_cqs = min_t(unsigned int, dev->num_comp_vectors, num_online_cpus());
398: for (i = 0; i < nr_cqs; i++) {
399: cq = ib_alloc_cq(dev, NULL, nr_cqes, i, poll_ctx);
]
|
| 8/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 8172
output: 64
thoughts: 256
Thoughts: **My Assessment of the Infiniband CQ Pool Cleanup Warning**
Okay, so I'm looking at this `WARN_ON(cq->cqe_used)` in `ib_cq_pool_cleanup`, triggered during an infiniband device unregistration. My immediate concern is the meaning of `cq->cqe_used` being non-zero here. That's a red flag; it signifies there are still active Completion Queue Entries (CQEs) tied to this Completion Queue (CQ) when it's supposed to be cleaned up. This strongly suggests a resource leak or, worse, a flawed cleanup process before the device is unregistered.
Exploitable? That's the key question. It's a `WARNING`, so the code *continues* execution after the warning. It then proceeds to `list_del(&cq->pool_entry); cq->shared = false; ib_free_cq(cq);`. The problem is, if `cq->cqe_used` *is* non-zero, this implies something else in the system still holds a reference to the CQ, or believes it *has* allocated CQEs from this CQ. If `ib_free_cq(cq)` is called before these references are cleared, the CQ is freed prematurely. Now the other component that's still using the CQ is in trouble.
Then it hits me: `cq->cqe_used` is an integer, literally tracking the number of in-use CQEs. This is critical. The implication is, some other part of the system is *still* pointing to this CQ or believes it still owns the CQEs! So, if that other component tries to use this freed CQ later, or free the CQEs it believes it owns, that's a classic use-after-free vulnerability! This is a dangerous situation. I need to trace back where `cqe_used` is modified to understand what's happening.
|
| 9/2 |
2026/08/25 01:30 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c IncludeSnippetLines:2 Name:ib_cq::cqe_used SourceTreePrefix:drivers/infiniband/]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_cleanup SourceFile:drivers/infiniband/core/cq.c SourceLine:370 SourceSnippet: 368: list_for_each_entry_safe(cq, n, &dev->cq_pools[i],
369: pool_entry) {
370: WARN_ON(cq->cqe_used);
371: list_del(&cq->pool_entry);
372: cq->shared = false;
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:476 SourceSnippet: 474: if (vector != cq->comp_vector)
475: continue;
476: if (cq->cqe_used + nr_cqe > cq->cqe)
477: continue;
478: found = cq;
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:483 SourceSnippet: 481:
482: if (found) {
483: found->cqe_used += nr_cqe;
484: spin_unlock_irq(&dev->cq_pools_lock);
485:
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_put SourceFile:drivers/infiniband/core/cq.c SourceLine:510 SourceSnippet: 508: void ib_cq_pool_put(struct ib_cq *cq, unsigned int nr_cqe)
509: {
510: if (WARN_ON_ONCE(nr_cqe > cq->cqe_used))
511: return;
512:
] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_put SourceFile:drivers/infiniband/core/cq.c SourceLine:514 SourceSnippet: 512:
513: spin_lock_irq(&cq->device->cq_pools_lock);
514: cq->cqe_used -= nr_cqe;
515: spin_unlock_irq(&cq->device->cq_pools_lock);
516: }
] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_free_cq SourceFile:drivers/infiniband/core/cq.c SourceLine:327 SourceSnippet: 325: if (WARN_ON_ONCE(atomic_read(&cq->usecnt)))
326: return;
327: if (WARN_ON_ONCE(cq->cqe_used))
328: return;
329:
]] TruncatedOutput:false]
|
| 10/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 9754
output: 40
thoughts: 283
Thoughts: **My Analysis of Shared CQ Cleanup**
Okay, so I'm looking at this shared completion queue (CQ) allocation and deallocation code. `ib_cq_pool_get` allocates CQEs from a shared CQ, and `ib_cq_pool_put` releases them. Straightforward enough. Now, I see this `ib_cq_pool_cleanup` function, and it seems like it's meant to clean up these shared CQs. The critical point is that if `cqe_used` is not zero, that indicates a user still has a CQE allocated but hasn't released it, triggering the following warning:
```c
list_for_each_entry_safe(cq, n, &dev->cq_pools[i],
pool_entry) {
WARN_ON(cq->cqe_used);
list_del(&cq->pool_entry);
cq->shared = false;
ib_free_cq(cq);
}
```
Now, let's look at `ib_free_cq`. It has this:
```c
if (WARN_ON_ONCE(atomic_read(&cq->usecnt)))
return;
if (WARN_ON_ONCE(cq->cqe_used))
return;
```
Hold on a second... if `cq->cqe_used` is non-zero, meaning CQEs are still in use, `ib_free_cq` *returns immediately*! It *doesn't* free the CQ! This contradicts the intention of `ib_cq_pool_cleanup`. It appears to simply remove the CQ from the pool, but the CQ itself remains allocated if some user has not called `ib_cq_pool_put` properly. This means the cleanup function will fail, which is clearly not intended, and there will be memory leaks if the shared CQ is not properly released. I need to investigate the circumstances under which `cqe_used` would not be zero during cleanup. I must check the `ib_free_cq` implementation further to get to the root of this behavior.
|
| 11/2 |
2026/08/25 01:30 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/core/cq.c FirstLine:320 LineCount:40]
Results: map[Contents: 320: */
321: void ib_free_cq(struct ib_cq *cq)
322: {
323: int ret = 0;
324:
325: if (WARN_ON_ONCE(atomic_read(&cq->usecnt)))
326: return;
327: if (WARN_ON_ONCE(cq->cqe_used))
328: return;
329:
330: rdma_restrack_del(&cq->res);
331: if (cq->device->ops.pre_destroy_cq) {
332: ret = cq->device->ops.pre_destroy_cq(cq);
333: WARN_ONCE(ret, "Disable of kernel CQ shouldn't fail");
334: }
335:
336: switch (cq->poll_ctx) {
337: case IB_POLL_DIRECT:
338: break;
339: case IB_POLL_SOFTIRQ:
340: irq_poll_disable(&cq->iop);
341: break;
342: case IB_POLL_WORKQUEUE:
343: case IB_POLL_UNBOUND_WORKQUEUE:
344: cancel_work_sync(&cq->work);
345: break;
346: default:
347: WARN_ON_ONCE(1);
348: }
349:
350: rdma_dim_destroy(cq);
351: trace_cq_free(cq);
352: if (cq->device->ops.post_destroy_cq)
353: cq->device->ops.post_destroy_cq(cq);
354: else
355: ret = cq->device->ops.destroy_cq(cq, NULL);
356: WARN_ONCE(ret, "Destroy of kernel CQ shouldn't fail");
357: kfree(cq->wc);
358: kfree(cq);
359: }
]
|
| 12/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 11128
output: 41
thoughts: 223
Thoughts: **Potential CQ Leak and Use-After-Free Vulnerability**
Okay, so here's the situation. I've got this code, and it looks like there's a potential issue with the way completion queues (CQs) are handled. Specifically, if `cq->cqe_used` isn't zero, `ib_free_cq` bails out early, meaning it doesn't actually free the CQ. The problem is, `ib_cq_pool_cleanup` has *already* removed the CQ from the pool's list using `list_del(&cq->pool_entry)`. So, we're leaking the CQ. Just a memory leak? Or something worse?
My primary concern, though, is whether this leak opens the door to a use-after-free scenario. If the device is being unregistered, which ultimately frees the device structure itself. The CQ holds a pointer to the device (`cq->device`). If we leak the CQ, and then *later* someone (let's say a forgetful user) tries to use it or free it, that access could potentially hit freed memory, specifically the freed device structure. That's a definite red flag.
But wait, hold on a second. If the user *still* has a reference to the CQ, does that also imply a continued reference to the device? Usually, IB objects do hold a reference to the device to prevent these problems. Or, device unregistration would wait for all of its children to be freed. I need to be sure about this. The critical piece of this puzzle is how `ib_unregister_device` handles references. Let's dig into that and see how that's addressed.
|
| 13/2 |
2026/08/25 01:30 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/core/device.c FirstLine:1550 LineCount:50]
Results: map[Contents:1550: mutex_lock(&ib_dev->subdev_lock);
1551: list_for_each_entry_safe_reverse(sub, tmp,
1552: &ib_dev->subdev_list_head,
1553: subdev_list) {
1554: list_del(&sub->subdev_list);
1555: ib_dev->ops.del_sub_dev(sub);
1556: ib_device_put(ib_dev);
1557: }
1558: mutex_unlock(&ib_dev->subdev_lock);
1559:
1560: /*
1561: * We have a registration lock so that all the calls to unregister are
1562: * fully fenced, once any unregister returns the device is truly
1563: * unregistered even if multiple callers are unregistering it at the
1564: * same time. This also interacts with the registration flow and
1565: * provides sane semantics if register and unregister are racing.
1566: */
1567: mutex_lock(&ib_dev->unregistration_lock);
1568: if (!refcount_read(&ib_dev->refcount))
1569: goto out;
1570:
1571: disable_device(ib_dev);
1572: rdma_nl_notify_event(ib_dev, 0, RDMA_UNREGISTER_EVENT);
1573:
1574: /* Expedite removing unregistered pointers from the hash table */
1575: free_netdevs(ib_dev);
1576:
1577: ib_free_port_attrs(&ib_dev->coredev);
1578: device_del(&ib_dev->dev);
1579: ib_device_unregister_rdmacg(ib_dev);
1580: ib_cache_cleanup_one(ib_dev);
1581:
1582: /*
1583: * Drivers using the new flow may not call ib_dealloc_device except
1584: * in error unwind prior to registration success.
1585: */
1586: if (ib_dev->ops.dealloc_driver &&
1587: ib_dev->ops.dealloc_driver != prevent_dealloc_device) {
1588: WARN_ON(kref_read(&ib_dev->dev.kobj.kref) <= 1);
1589: ib_dealloc_device(ib_dev);
1590: }
1591: out:
1592: mutex_unlock(&ib_dev->unregistration_lock);
1593: }
1594:
1595: /**
1596: * ib_unregister_device - Unregister an IB device
1597: * @ib_dev: The device to unregister
1598: *
1599: * Unregister an IB device. All clients will receive a remove callback.
]
|
| 14/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 12562
output: 41
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Checking `disable_device`**
Alright, time to dive in. My first thought is to zero in on this `disable_device` function or variable β whatever it is. Knowing my work, it's probably a crucial control point, perhaps a flag, a configuration setting, or even a method call responsible for, well, disabling a device. Given the context, I immediately need to establish a baseline. What is this `disable_device` entity?
My initial questions would likely revolve around its implementation. Where is this thing defined? What code path touches it? What values can it assume (if it's a variable), or what behaviors does it trigger (if it's a function)? Is this value being set locally, or pulled from some shared configuration or database? Does it disable the device directly, or does it signal another component? In essence, before I can reason further, I need to know where I am.
Ultimately, I want to form an informed mental model of what `disable_device` does, in the context of the larger architecture. I want to build a better understanding of how the system works and how the device will be affected, should the code use or fail to use this "disable" function. That's the first step.
|
| 15/2 |
2026/08/25 01:30 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/core/device.c FirstLine:1310 LineCount:50]
Results: map[Contents:1310: down_write(&devices_rwsem);
1311: xa_clear_mark(&devices, device->index, DEVICE_REGISTERED);
1312: up_write(&devices_rwsem);
1313:
1314: /*
1315: * Remove clients in LIFO order, see assign_client_id. This could be
1316: * more efficient if xarray learns to reverse iterate. Since no new
1317: * clients can be added to this ib_device past this point we only need
1318: * the maximum possible client_id value here.
1319: */
1320: down_read(&clients_rwsem);
1321: cid = highest_client_id;
1322: up_read(&clients_rwsem);
1323: while (cid) {
1324: cid--;
1325: remove_client_context(device, cid);
1326: }
1327:
1328: ib_cq_pool_cleanup(device);
1329:
1330: /* Pairs with refcount_set in enable_device */
1331: ib_device_put(device);
1332: wait_for_completion(&device->unreg_completion);
1333:
1334: /*
1335: * compat devices must be removed after device refcount drops to zero.
1336: * Otherwise init_net() may add more compatdevs after removing compat
1337: * devices and before device is disabled.
1338: */
1339: remove_compat_devs(device);
1340: }
1341:
1342: /*
1343: * An enabled device is visible to all clients and to all the public facing
1344: * APIs that return a device pointer. This always returns with a new get, even
1345: * if it fails.
1346: */
1347: static int enable_device_and_get(struct ib_device *device)
1348: {
1349: struct ib_client *client;
1350: unsigned long index;
1351: int ret = 0;
1352:
1353: /*
1354: * One ref belongs to the xa and the other belongs to this
1355: * thread. This is needed to guard against parallel unregistration.
1356: */
1357: refcount_set(&device->refcount, 2);
1358: down_write(&devices_rwsem);
1359: xa_set_mark(&devices, device->index, DEVICE_REGISTERED);
]
|
| 16/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 13639
output: 41
thoughts: 125
Thoughts: **My Reasoning on Device Deregistration**
Okay, so `disable_device` kicks things off by waiting on `device->unreg_completion`. Makes sense; that's the point where we know the device is truly unregistered. Specifically, `wait_for_completion(&device->unreg_completion);` is the key. That completion gets signaled when the device's reference count hits zero. But that's where I start to wonder about the ordering.
The concern is the Critical Queue (CQ). If `cq->cqe_used` isn't zero, it implies something's still actively using the CQ. But `ib_cq_pool_cleanup` gets called *before* that `wait_for_completion`. So, if the CQ isn't cleaned up (i.e. released), does it potentially maintain a reference to the device, in some way? That could create a race or prevent the refcount from dropping to zero as expected. I need to dig into `ib_alloc_cq` to understand the CQ's device association and confirm my thinking. It's a key function to examine for device reference management.
|
| 17/2 |
2026/08/25 01:30 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_alloc_cq]
Results: map[SourceCode:4167: static inline struct ib_cq *ib_alloc_cq(struct ib_device *dev, void *private,
4168: int nr_cqe, int comp_vector,
4169: enum ib_poll_context poll_ctx)
4170: {
4171: return __ib_alloc_cq(dev, private, nr_cqe, comp_vector, poll_ctx,
4172: KBUILD_MODNAME);
4173: }
SourceFile:include/rdma/ib_verbs.h]
|
| 18/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 14208
output: 42
thoughts: 12
Thoughts: Here's the summary, assuming I'm an expert in InfiniBand/RDMA:
**Checking the Allocation of Completion Queues**
Alright, let's dive right in and take a look at `__ib_alloc_cq`. This likely means I'm about to scrutinize the code responsible for allocating Completion Queues (CQs) within the InfiniBand/RDMA stack. This function is absolutely critical; it's the gatekeeper that provides CQs, which are fundamental for handling the completion events of our RDMA operations. I need to understand how it's managing resources, whether it's thread-safe, and if there are any potential bottlenecks lurking within. I'll be looking for things like memory allocation strategies, error handling related to resource exhaustion, and any potential lock contention that could impact performance under heavy load. I'm especially interested in how it interacts with the underlying hardware and the HCA drivers. Is it directly interacting with hardware registers, or are there layers of abstraction in place? Depending on the architecture I'm working with, I'll need to look at how it manages user and kernel context CQs and any related context switching overhead, and ensure we're getting the best performance.
|
| 19/2 |
2026/08/25 01:30 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:__ib_alloc_cq]
Results: map[SourceCode: 198: /**
199: * __ib_alloc_cq - allocate a completion queue
200: * @dev: device to allocate the CQ for
201: * @private: driver private data, accessible from cq->cq_context
202: * @nr_cqe: number of CQEs to allocate
203: * @comp_vector: HCA completion vectors for this CQ
204: * @poll_ctx: context to poll the CQ from.
205: * @caller: module owner name.
206: *
207: * This is the proper interface to allocate a CQ for in-kernel users. A
208: * CQ allocated with this interface will automatically be polled from the
209: * specified context. The ULP must use wr->wr_cqe instead of wr->wr_id
210: * to use this CQ abstraction.
211: */
212: struct ib_cq *__ib_alloc_cq(struct ib_device *dev, void *private, int nr_cqe,
213: int comp_vector, enum ib_poll_context poll_ctx,
214: const char *caller)
215: {
216: struct ib_cq_init_attr cq_attr = {
217: .cqe = nr_cqe,
218: .comp_vector = comp_vector,
219: };
220: struct ib_cq *cq;
221: int ret = -ENOMEM;
222:
223: if (WARN_ON_ONCE(!nr_cqe))
224: return ERR_PTR(-EINVAL);
225:
226: cq = rdma_zalloc_drv_obj(dev, ib_cq);
227: if (!cq)
228: return ERR_PTR(ret);
229:
230: cq->device = dev;
231: cq->cq_context = private;
232: cq->poll_ctx = poll_ctx;
233: atomic_set(&cq->usecnt, 0);
234: cq->comp_vector = comp_vector;
235:
236: cq->wc = kmalloc_objs(*cq->wc, IB_POLL_BATCH);
237: if (!cq->wc)
238: goto out_free_cq;
239:
240: rdma_restrack_new(&cq->res, RDMA_RESTRACK_CQ);
241: rdma_restrack_set_name(&cq->res, caller);
242:
243: ret = dev->ops.create_cq(cq, &cq_attr, NULL);
244: if (ret)
245: goto out_free_wc;
246:
247: rdma_dim_init(cq);
248:
249: switch (cq->poll_ctx) {
250: case IB_POLL_DIRECT:
251: cq->comp_handler = ib_cq_completion_direct;
252: break;
253: case IB_POLL_SOFTIRQ:
254: cq->comp_handler = ib_cq_completion_softirq;
255:
256: irq_poll_init(&cq->iop, IB_POLL_BUDGET_IRQ, ib_poll_handler);
257: ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
258: break;
259: case IB_POLL_WORKQUEUE:
260: case IB_POLL_UNBOUND_WORKQUEUE:
261: cq->comp_handler = ib_cq_completion_workqueue;
262: INIT_WORK(&cq->work, ib_cq_poll_work);
263: ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
264: cq->comp_wq = (cq->poll_ctx == IB_POLL_WORKQUEUE) ?
265: ib_comp_wq : ib_comp_unbound_wq;
266: break;
267: default:
268: ret = -EINVAL;
269: goto out_destroy_cq;
270: }
271:
272: rdma_restrack_add(&cq->res);
273: trace_cq_alloc(cq, nr_cqe, comp_vector, poll_ctx);
274: return cq;
275:
276: out_destroy_cq:
277: rdma_dim_destroy(cq);
278: cq->device->ops.destroy_cq(cq, NULL);
279: out_free_wc:
280: rdma_restrack_put(&cq->res);
281: kfree(cq->wc);
282: out_free_cq:
283: kfree(cq);
284: trace_cq_alloc_error(nr_cqe, comp_vector, poll_ctx, ret);
285: return ERR_PTR(ret);
286: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 20/2 |
2026/08/25 01:30 |
llm |
expert |
0m
Model:
Tokens:
input: 16001
output: 32
thoughts: 282
Thoughts: **Debugging a Potential CQE Leak**
Okay, so `ib_alloc_cq` *doesn't* increment the device's reference count, which is a key point to remember. But then, `disable_device` removes clients, and I know that includes all the ULPs like smbdirect, rds, iser, and so on. It iterates through client IDs and calls `remove_client_context` for each. That function is crucial; it triggers the `remove` callback for each client.
Now, the important thing here is that the `remove` callback *should* free all resources, and that *should* include returning any shared CQs that were acquired via `ib_cq_pool_get`. If `cq->cqe_used` is still non-zero after all clients are removed, it's a leak; one of the clients failed to properly release its CQEs.
The bug report is very specific: "smbdirect: ib_dev[syz1] removed". That's our smoking gun, right? It means `smbdirect` was the client being removed. And then `ib_cq_pool_cleanup` is warning about a non-zero `cq->cqe_used`. So, it's highly probable that `smbdirect` is leaking those CQEs.
My next step is clear: I need to dive into the `smbdirect` code and meticulously check every instance of `ib_cq_pool_get` to verify how it's being used. I need to trace through the release path and make sure everything is handled correctly, particularly in its `remove` callback. This should pinpoint the exact location of the leak, if it's there.
|
| 21/2 |
2026/08/25 01:31 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:fs/smb/]
Results:
|
| 22/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 16694
output: 29
thoughts: 37
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Where's That Dang `smbdirect`?**
Okay, so I'm digging into this SMB client code... and I need to track down the implementation of `smbdirect`. Now, the nagging question is: where the *heck* did they put it? Is it nestled in the older CIFS code, specifically `fs/cifs/smbdirect.c`, or did they consolidate it within the more recent SMB implementation, residing within `fs/smb/client/smbdirect.c`? It's a fundamental question as it dictates the base of the code I'll need to work with. Time to check and see what the newer codebase did.
|
| 23/2 |
2026/08/25 01:31 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:fs/]
Results:
|
| 24/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 16937
output: 27
thoughts: 8
Thoughts: Here's a summary of my thinking, expressed in the first person:
**Locating the `smbdirect` Module**
Okay, so I'm trying to figure out where `smbdirect` is located. This is a crucial first step. I need to know its path so I can interact with it, whether that's for configuration, troubleshooting, or even just confirming its existence. Is it a system-level module, a user-specific package, or something else entirely? Knowing the directory structure is paramount for any further interaction. Let's see if I can find it quickly.
|
| 25/2 |
2026/08/25 01:31 |
tool |
grepper |
0m
Args: map[Expression:ib_cq_pool_get PathPrefix:]
Results: map[Output:drivers/infiniband/core/cq.c=378=static int ib_alloc_cqs(struct ib_device *dev, unsigned int nr_cqes,
--
drivers/infiniband/core/cq.c-422-/**
drivers/infiniband/core/cq.c:423: * ib_cq_pool_get() - Find the least used completion queue that matches
drivers/infiniband/core/cq.c-424- * a given cpu hint (or least used for wild card affinity) and fits
--
drivers/infiniband/core/cq.c-437- */
drivers/infiniband/core/cq.c:438:struct ib_cq *ib_cq_pool_get(struct ib_device *dev, unsigned int nr_cqe,
drivers/infiniband/core/cq.c-439- int comp_vector_hint,
--
drivers/infiniband/core/cq.c-500-}
drivers/infiniband/core/cq.c:501:EXPORT_SYMBOL(ib_cq_pool_get);
drivers/infiniband/core/cq.c-502-
--
drivers/infiniband/ulp/iser/iser_verbs.c=228=static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
--
drivers/infiniband/ulp/iser/iser_verbs.c-249- cq_size = max_send_wr + ISER_QP_MAX_RECV_DTOS;
drivers/infiniband/ulp/iser/iser_verbs.c:250: ib_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_SOFTIRQ);
drivers/infiniband/ulp/iser/iser_verbs.c-251- if (IS_ERR(ib_conn->cq)) {
--
drivers/infiniband/ulp/isert/ib_isert.c=102=isert_create_qp(struct isert_conn *isert_conn,
--
drivers/infiniband/ulp/isert/ib_isert.c-110-
drivers/infiniband/ulp/isert/ib_isert.c:111: isert_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_WORKQUEUE);
drivers/infiniband/ulp/isert/ib_isert.c-112- if (IS_ERR(isert_conn->cq)) {
--
drivers/infiniband/ulp/rtrs/rtrs.c=232=static int create_cq(struct rtrs_con *con, int cq_vector, int nr_cqe,
--
drivers/infiniband/ulp/rtrs/rtrs.c-241- else
drivers/infiniband/ulp/rtrs/rtrs.c:242: cq = ib_cq_pool_get(cm_id->device, nr_cqe, cq_vector, poll_ctx);
drivers/infiniband/ulp/rtrs/rtrs.c-243-
--
drivers/infiniband/ulp/srpt/ib_srpt.c=1854=static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-1870-retry:
drivers/infiniband/ulp/srpt/ib_srpt.c:1871: ch->cq = ib_cq_pool_get(sdev->device, ch->rq_size + sq_size, -1,
drivers/infiniband/ulp/srpt/ib_srpt.c-1872- IB_POLL_WORKQUEUE);
--
drivers/nvme/host/rdma.c=472=static int nvme_rdma_create_cq(struct ib_device *ibdev,
--
drivers/nvme/host/rdma.c-487- else
drivers/nvme/host/rdma.c:488: queue->ib_cq = ib_cq_pool_get(ibdev, queue->cq_size,
drivers/nvme/host/rdma.c-489- comp_vector, IB_POLL_SOFTIRQ);
--
drivers/nvme/target/rdma.c=1274=static int nvmet_rdma_create_queue_ib(struct nvmet_rdma_queue *queue)
--
drivers/nvme/target/rdma.c-1284-
drivers/nvme/target/rdma.c:1285: queue->cq = ib_cq_pool_get(ndev->device, nr_cqe + 1,
drivers/nvme/target/rdma.c-1286- queue->comp_vector, IB_POLL_WORKQUEUE);
--
include/rdma/ib_verbs.h=4293=static inline int ib_req_notify_cq(struct ib_cq *cq,
--
include/rdma/ib_verbs.h-4298-
include/rdma/ib_verbs.h:4299:struct ib_cq *ib_cq_pool_get(struct ib_device *dev, unsigned int nr_cqe,
include/rdma/ib_verbs.h-4300- int comp_vector_hint,
]
|
| 26/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 18463
output: 30
thoughts: 266
Thoughts: **My Investigation into `smbdirect` and CQ Management**
Okay, so I'm digging into how `smbdirect` handles Completion Queues (CQs). My initial assumption was that `smbdirect` might be directly utilizing `ib_cq_pool_get`. But hang on, that doesn't seem to be the case. Now I'm wondering, if it *doesn't* call `ib_cq_pool_get` directly, does it potentially use it indirectly through `ib_alloc_cq`?
Aha, no, `ib_alloc_cq` is a wrapper function; it calls `__ib_alloc_cq`. Right, I need to remind myself where `ib_alloc_cq` is defined: `include/rdma/ib_verbs.h`. So `ib_alloc_cq` *is* a wrapper around `__ib_alloc_cq`. Got it.
But `ib_cq_pool_get`, as I recall, is for shared CQs, right? Okay, time to circle back to the core issue. I'm focusing on why a shared CQ might be left in a non-clean state. `ib_cq_pool_cleanup` checks the `cq->cqe_used` field. If `cqe_used` is non-zero, then something called `ib_cq_pool_get` at some point. But where? And why didn't `ib_cq_pool_put` get called later?
So, who's responsible for calling `ib_cq_pool_get`? Need to check `ib_alloc_cq` vs `ib_alloc_cq_any` to see if the allocation functions are the culprit. Wait a minute, `ib_alloc_cq` specifically *doesn't* use `ib_cq_pool_get`. Hmm. So where is this shared CQ being allocated?
Right, back to the starting point. Does `smbdirect` use `ib_alloc_cq`?
|
| 27/2 |
2026/08/25 01:31 |
tool |
grepper |
0m
Args: map[Expression:ib_alloc_cq PathPrefix:fs/smb/]
Results: map[Output:fs/smb/smbdirect/connection.c=242=int smbdirect_connection_create_qp(struct smbdirect_socket *sc)
--
fs/smb/smbdirect/connection.c-346-
fs/smb/smbdirect/connection.c:347: sc->ib.send_cq = ib_alloc_cq_any(sc->ib.dev, sc,
fs/smb/smbdirect/connection.c-348- max_send_wr,
--
fs/smb/smbdirect/connection.c-356-
fs/smb/smbdirect/connection.c:357: sc->ib.recv_cq = ib_alloc_cq_any(sc->ib.dev, sc,
fs/smb/smbdirect/connection.c-358- qp_cap.max_recv_wr,
--
fs/smb/smbdirect/connection.c=399=void smbdirect_connection_destroy_qp(struct smbdirect_socket *sc)
--
fs/smb/smbdirect/connection.c-406- /*
fs/smb/smbdirect/connection.c:407: * These CQs were created with ib_alloc_cq_any(), which arms an internal
fs/smb/smbdirect/connection.c-408- * completion handler (ib_cq_poll_work for IB_POLL_WORKQUEUE). They MUST be
]
|
| 28/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 19563
output: 43
thoughts: 31
Thoughts: Okay, here's my interpretation of those thoughts, framed as an expert's internal monologue:
**Investigating the CQ Allocation in smbdirect**
Right, `smbdirect` is using `ib_alloc_cq_any`. That's interesting, and potentially revealing. I need to understand exactly how it's handling Completion Queue (CQ) allocation. Let's dig into `ib_alloc_cq_any` and see what's going on under the hood. It could impact performance significantly if not done carefully. I need to understand the implications of this particular allocation strategy.
|
| 29/2 |
2026/08/25 01:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_alloc_cq_any]
Results: map[SourceCode:4179: /**
4180: * ib_alloc_cq_any: Allocate kernel CQ
4181: * @dev: The IB device
4182: * @private: Private data attached to the CQE
4183: * @nr_cqe: Number of CQEs in the CQ
4184: * @poll_ctx: Context used for polling the CQ
4185: */
4186: static inline struct ib_cq *ib_alloc_cq_any(struct ib_device *dev,
4187: void *private, int nr_cqe,
4188: enum ib_poll_context poll_ctx)
4189: {
4190: return __ib_alloc_cq_any(dev, private, nr_cqe, poll_ctx,
4191: KBUILD_MODNAME);
4192: }
SourceFile:include/rdma/ib_verbs.h]
|
| 30/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 20026
output: 44
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Diving into `__ib_alloc_cq_any`**
Okay, let's get right to it. My initial thought process is always to start at the foundational elements, especially when dealing with InfiniBand. I see the name `__ib_alloc_cq_any` and immediately recognize it as a core allocation function, specifically related to completion queues. I know completion queues are *crucial* for efficient communication, so this functionβs behavior dictates a significant portion of my overall performance. I need to understand exactly how this allocation is handled at this level. This function likely orchestrates the instantiation of CQs with varying parameters, and I'll need to scrutinize how it decides which physical resources to assign and how it interacts with the underlying hardware, so let's start with a deep dive.
|
| 31/2 |
2026/08/25 01:31 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:__ib_alloc_cq_any]
Results: map[SourceCode: 289: /**
290: * __ib_alloc_cq_any - allocate a completion queue
291: * @dev: device to allocate the CQ for
292: * @private: driver private data, accessible from cq->cq_context
293: * @nr_cqe: number of CQEs to allocate
294: * @poll_ctx: context to poll the CQ from
295: * @caller: module owner name
296: *
297: * Attempt to spread ULP Completion Queues over each device's interrupt
298: * vectors. A simple best-effort mechanism is used.
299: */
300: struct ib_cq *__ib_alloc_cq_any(struct ib_device *dev, void *private,
301: int nr_cqe, enum ib_poll_context poll_ctx,
302: const char *caller)
303: {
304: static atomic_t counter;
305: int comp_vector = 0;
306:
307: if (dev->num_comp_vectors > 1)
308: comp_vector =
309: atomic_inc_return(&counter) %
310: min_t(int, dev->num_comp_vectors, num_online_cpus());
311:
312: return __ib_alloc_cq(dev, private, nr_cqe, comp_vector, poll_ctx,
313: caller);
314: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 32/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 20737
output: 30
thoughts: 255
Thoughts: **My Analysis of the RDMA CQ Allocation and Cleanup Logic**
Okay, let's break this down. First, I noticed that `__ib_alloc_cq_any` calls `__ib_alloc_cq` directly, bypassing the `ib_cq_pool_get` function. This strongly suggests that if `smbdirect` is using `ib_alloc_cq_any`, it's allocating its own completion queues (CQs). It's *not* tapping into the shared CQ pool, which is the key point.
So, here's the puzzle: If `smbdirect` isn't using the shared pool, why is `ib_cq_pool_cleanup` even complaining about non-zero `cq->cqe_used` counts? Then, it hit me... `ib_cq_pool_cleanup` iterates through `dev->cq_pools`. That's where the confusion comes from. These are the *shared* CQs that were allocated during device registration using `ib_alloc_cqs`.
But, wait a minute, the bug report says `smbdirect: ib_dev[syz1] removed`. If it's not using the shared pool, the removal shouldn't be related, should it? Maybe `smbdirect` is just a module that was loaded. The "removed" message could simply indicate its removal from the system, or that it printed an informational message when the device was removed.
Okay, let's step back and consider the bug report: `smbdirect: ib_dev[syz1] removed`. It's pretty clear that this message is printed by `smbdirect` when its `remove` callback is called. It would make sense to go check the callback function.
|
| 33/2 |
2026/08/25 01:31 |
tool |
grepper |
0m
Args: map[Expression:ib_dev.*removed PathPrefix:fs/smb/]
Results: map[Output:fs/smb/smbdirect/devices.c=128=static void smbdirect_ib_client_remove(struct ib_device *ib_dev, void *client_data)
--
fs/smb/smbdirect/devices.c-135- list_del(&sdev->list);
fs/smb/smbdirect/devices.c:136: pr_info("ib_dev[%.*s] removed\n",
fs/smb/smbdirect/devices.c-137- IB_DEVICE_NAME_MAX, sdev->ib_name);
]
|
| 34/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 21552
output: 56
thoughts: 86
Thoughts: **Investigating the `smbdirect` CQE Leak**
Okay, so it looks like `smbdirect` is just *one* of the clients being flagged for removal. The warning is coming from `ib_cq_pool_cleanup`, which immediately suggests that the root cause is probably some other client (or potentially `smbdirect` itself, if it calls `ib_cq_pool_get` anywhere else) leaking CQEs from the shared CQ pool. The key here is to figure out which component is incorrectly managing the CQE lifecycle. The most logical next step is to trace all calls to `ib_cq_pool_get` to see where the shared CQ pool is being accessed and utilized, and then, to see where CQEs are not being correctly returned. This will help determine the origin of the leak. Let's dig into that function's usage.
|
| 35/2 |
2026/08/25 01:31 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c IncludeSnippetLines:0 Name:ib_cq_pool_get SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:create_cq SourceFile:drivers/infiniband/ulp/rtrs/rtrs.c SourceLine:242] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:iser_create_ib_conn_res SourceFile:drivers/infiniband/ulp/iser/iser_verbs.c SourceLine:250] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvme_rdma_create_cq SourceFile:drivers/nvme/host/rdma.c SourceLine:488] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_rdma_create_queue_ib SourceFile:drivers/nvme/target/rdma.c SourceLine:1285] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__UNIQUE_ID_addressable_ib_cq_pool_get_1650 SourceFile:drivers/infiniband/core/cq.c SourceLine:501] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:501] map[ReferenceKind:takes-address-of ReferencingEntityKind:global_variable ReferencingEntityName:__gendwarfksyms_ptr_ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:501]] TruncatedOutput:false]
|
| 36/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 22350
output: 57
thoughts: 195
Thoughts: **Investigating Unexpected CQE Utilization**
Okay, so `ib_cq_pool_get` is called within quite a few different infiniband and NVMe RDMA drivers: `rtrs.c`, `iser_verbs.c`, `rdma.c` (both host and target), `ib_srpt.c`, and `ib_isert.c`. That's a solid list of users. But, I'm still not connecting the dots to the original problem.
The core of the issue is that `cq->cqe_used` is apparently non-zero even though the presumed drivers aren't loaded. That doesn't make logical sense. If these modules aren't active, how could the completion queue's usage counter be anything other than zero? Is there some other mechanism, some *other* place, that modifies `cqe_used`? It's time to go back and double-check those references to `cqe_used` and ensure I haven't missed a crucial detail. There *has* to be a missing piece of the puzzle here.
|
| 37/2 |
2026/08/25 01:31 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c IncludeSnippetLines:0 Name:ib_cq::cqe_used SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_cleanup SourceFile:drivers/infiniband/core/cq.c SourceLine:370] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:476] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_get SourceFile:drivers/infiniband/core/cq.c SourceLine:483] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_put SourceFile:drivers/infiniband/core/cq.c SourceLine:510] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:ib_cq_pool_put SourceFile:drivers/infiniband/core/cq.c SourceLine:514] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:ib_free_cq SourceFile:drivers/infiniband/core/cq.c SourceLine:327]] TruncatedOutput:false]
|
| 38/2 |
2026/08/25 01:31 |
llm |
expert |
0m
Model:
Tokens:
input: 23188
output: 32
thoughts: 356
Thoughts: **Debugging a Possible CQE Leak**
Okay, so I'm looking at this issue, and the problem seems to stem from a modification to `cqe_used`. The code only shows `ib_cq_pool_get` and `ib_cq_pool_put` modifying it, which points to a ULP leaking a CQE. The key is to find out *which* ULP. Since `ib_cq_pool_get` was called, that means a ULP had to have been active at some point, meaning it was invoked.
My next line of thinking is, can any of these ULPs be triggered by an unprivileged user? If so, then it's a huge security risk. Let's see... `nvme-rdma` requires writing to `/dev/nvme-fabrics`, root-only. Same for `nvmet-rdma`, as that's configured through configfs. `iser`, `isert`, `rtrs`, and `srpt` are all configured via either netlink, sysfs, or configfs, all requiring root access. So, that's a dead end.
Hang on... is there *any* ULP accessible to unprivileged users? `rds` uses RDMA, but doesn't call `ib_cq_pool_get`. Same with `smbdirect`, another RDMA implementation. Hmmm... is it possible that `ib_cq_pool_get` is called *outside* of these known ULPs? I need to verify if `ib_cq_pool_get` is even exported β aha! Yes, it's `EXPORT_SYMBOL(ib_cq_pool_get)`. That means it *could* be called by anything that knows the function's address. What about `rpcrdma`? That's the next avenue to explore.
|
| 39/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/sunrpc/]
Results:
|
| 40/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 23984
output: 32
thoughts: 10
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Initial Inquiry - Potential Alias**
Hmm, I'm trying to figure out how this thing is invoked. It makes me wonder, could this be called by something like `smc`? It's a fairly common shorthand or prefix, depending on the context. If it is invoked using the name `smc`, that could be significant for tracking its usage or integration. I'll need to look for that pattern in the codebase, the documentation, or any relevant configuration files. It's a hypothesis, but a starting point that might lead to clues on understanding its functionality. This reminds me, I need to check the command-line arguments to see if there's any obvious correspondence.
|
| 41/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/smc/]
Results:
|
| 42/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 24199
output: 31
thoughts: 9
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: Potential Abbreviation Considerations**
Hmm, I'm trying to figure out how this function or component might be invoked. My first thought is whether we can simplify the call syntax. Is it possible that this thing is accessible via `rds`? That abbreviation feels efficient and it's something I'd want to consider early on for readability and usability. It could be cleaner and faster to implement this way. I wonder if `rds` might be a relevant and acceptable way to reference it...
|
| 43/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/rds/]
Results:
|
| 44/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 24384
output: 42
thoughts: 77
Thoughts: Okay, here's my thought process as I would articulate it, keeping in mind my expertise:
**Debugging a Shared Completion Queue Issue**
Alright, this is puzzling. I'm looking at a situation where `cqe_used` seems to be non-zero, but I'm not seeing the expected calls to `ib_cq_pool_get`. Something isn't adding up. If the completion queue pool isn't being "gotten" from, how could its usage count be incremented? That's the fundamental question here.
My initial assumption is that perhaps `cqe_used` is simply uninitialized, which would lead to it having an arbitrary value. This could be a race condition, a compiler optimization that didn't go the way it was intended, or a memory corruption issue. But that doesn't feel right. There has to be a more fundamental issue.
I need to trace back from the point of the error. Given that the function in question is `ib_cq_pool_get`, I have to examine the initialization and allocation code. That means I need to start digging through `ib_alloc_cqs`, which, as I recall, handles the shared completion queue allocation. Let's see how that's done and if there is a mistake there.
|
| 45/2 |
2026/08/25 01:32 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_alloc_cqs]
Results: map[SourceCode: 378: static int ib_alloc_cqs(struct ib_device *dev, unsigned int nr_cqes,
379: enum ib_poll_context poll_ctx)
380: {
381: LIST_HEAD(tmp_list);
382: unsigned int nr_cqs, i;
383: struct ib_cq *cq, *n;
384: int ret;
385:
386: if (poll_ctx > IB_POLL_LAST_POOL_TYPE) {
387: WARN_ON_ONCE(poll_ctx > IB_POLL_LAST_POOL_TYPE);
388: return -EINVAL;
389: }
390:
391: /*
392: * Allocate at least as many CQEs as requested, and otherwise
393: * a reasonable batch size so that we can share CQs between
394: * multiple users instead of allocating a larger number of CQs.
395: */
396: nr_cqes = min(dev->attrs.max_cqe, max(nr_cqes, IB_MAX_SHARED_CQ_SZ));
397: nr_cqs = min_t(unsigned int, dev->num_comp_vectors, num_online_cpus());
398: for (i = 0; i < nr_cqs; i++) {
399: cq = ib_alloc_cq(dev, NULL, nr_cqes, i, poll_ctx);
400: if (IS_ERR(cq)) {
401: ret = PTR_ERR(cq);
402: goto out_free_cqs;
403: }
404: cq->shared = true;
405: list_add_tail(&cq->pool_entry, &tmp_list);
406: }
407:
408: spin_lock_irq(&dev->cq_pools_lock);
409: list_splice(&tmp_list, &dev->cq_pools[poll_ctx]);
410: spin_unlock_irq(&dev->cq_pools_lock);
411:
412: return 0;
413:
414: out_free_cqs:
415: list_for_each_entry_safe(cq, n, &tmp_list, pool_entry) {
416: cq->shared = false;
417: ib_free_cq(cq);
418: }
419: return ret;
420: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 46/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 25516
output: 34
thoughts: 106
Thoughts: **Analyzing the Infiniband Completion Queue Allocation**
Okay, so `ib_alloc_cq` clearly calls `__ib_alloc_cq`, which then immediately allocates the completion queue (`cq`) using `rdma_zalloc_drv_obj(dev, ib_cq)`. The key thing here is that `rdma_zalloc_drv_obj` leverages `kzalloc`. This is important because it guarantees that `cqe_used` is initialized to zero, which makes sense. If it weren't zeroed at allocation we'd have a serious issue!
This *strongly* suggests that some other mechanism, most likely `ib_cq_pool_get`, is responsible for managing the pool of completion queue entries. We need to be absolutely certain of this. To confirm this theory, and ensure proper understanding of how the CQ's are created and used, I really need to check if any other parts of the code actually use `ib_cq_pool_get`. Let's dig deeper and see.
|
| 47/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Args: map[Expression:ib_cq_pool_get PathPrefix:drivers/infiniband/]
Results: map[Output:drivers/infiniband/core/cq.c=378=static int ib_alloc_cqs(struct ib_device *dev, unsigned int nr_cqes,
--
drivers/infiniband/core/cq.c-422-/**
drivers/infiniband/core/cq.c:423: * ib_cq_pool_get() - Find the least used completion queue that matches
drivers/infiniband/core/cq.c-424- * a given cpu hint (or least used for wild card affinity) and fits
--
drivers/infiniband/core/cq.c-437- */
drivers/infiniband/core/cq.c:438:struct ib_cq *ib_cq_pool_get(struct ib_device *dev, unsigned int nr_cqe,
drivers/infiniband/core/cq.c-439- int comp_vector_hint,
--
drivers/infiniband/core/cq.c-500-}
drivers/infiniband/core/cq.c:501:EXPORT_SYMBOL(ib_cq_pool_get);
drivers/infiniband/core/cq.c-502-
--
drivers/infiniband/ulp/iser/iser_verbs.c=228=static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
--
drivers/infiniband/ulp/iser/iser_verbs.c-249- cq_size = max_send_wr + ISER_QP_MAX_RECV_DTOS;
drivers/infiniband/ulp/iser/iser_verbs.c:250: ib_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_SOFTIRQ);
drivers/infiniband/ulp/iser/iser_verbs.c-251- if (IS_ERR(ib_conn->cq)) {
--
drivers/infiniband/ulp/isert/ib_isert.c=102=isert_create_qp(struct isert_conn *isert_conn,
--
drivers/infiniband/ulp/isert/ib_isert.c-110-
drivers/infiniband/ulp/isert/ib_isert.c:111: isert_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_WORKQUEUE);
drivers/infiniband/ulp/isert/ib_isert.c-112- if (IS_ERR(isert_conn->cq)) {
--
drivers/infiniband/ulp/rtrs/rtrs.c=232=static int create_cq(struct rtrs_con *con, int cq_vector, int nr_cqe,
--
drivers/infiniband/ulp/rtrs/rtrs.c-241- else
drivers/infiniband/ulp/rtrs/rtrs.c:242: cq = ib_cq_pool_get(cm_id->device, nr_cqe, cq_vector, poll_ctx);
drivers/infiniband/ulp/rtrs/rtrs.c-243-
--
drivers/infiniband/ulp/srpt/ib_srpt.c=1854=static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-1870-retry:
drivers/infiniband/ulp/srpt/ib_srpt.c:1871: ch->cq = ib_cq_pool_get(sdev->device, ch->rq_size + sq_size, -1,
drivers/infiniband/ulp/srpt/ib_srpt.c-1872- IB_POLL_WORKQUEUE);
]
|
| 48/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 26836
output: 25
thoughts: 346
Thoughts: **Investigating the Root Cause of `ib_cq_pool_get` Usage**
Alright, let's break this down. It seems that `ib_cq_pool_get` is used by `iser`, `isert`, `rtrs`, `srpt`, `nvme-rdma`, and `nvmet-rdma`. My immediate concern is, if these components are *not* accessible to unprivileged users, how did syzbot manage to trigger this? Syzbot can't directly load modules, can it? Ah, but it *can* trigger auto-loading. So, could syzbot be poking at `nvme-rdma` or `nvmet-rdma` and causing the problem that way?
Perhaps syzbot is running these tests in a namespace with `CAP_NET_ADMIN` or `CAP_SYS_ADMIN`? Yes, syzbot does run in user namespaces with all capabilities, doesn't it? Okay, so that means it *could* configure `nvmet-rdma` through configfs, or interact with `nvme-rdma` via `/dev/nvme-fabrics`. Wait, hang on... configfs isn't mountable in user namespaces. And `/dev/nvme-fabrics` isn't accessible either, unless explicitly created or bind-mounted, which isn't the case in the syzbot environment. So those are dead ends.
What about `rtrs`? That uses sysfs, right? And sysfs isn't writable in user namespaces either. Okay, what about `iser`? That utilizes netlink, and *netlink* is accessible in user namespaces. Interesting... Let's check if `iser` can be triggered via netlink. `iser` is iSCSI Extensions for RDMA, so iSCSI definitely uses netlink. Let's start by digging into the `iscsi_transport` netlink interface.
|
| 49/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Args: map[Expression:iscsi_iser PathPrefix:]
Results: map[Output:Documentation/driver-api/infiniband.rst=73=iSCSI Extensions for RDMA (iSER)
--
Documentation/driver-api/infiniband.rst-75-
Documentation/driver-api/infiniband.rst:76:.. kernel-doc:: drivers/infiniband/ulp/iser/iscsi_iser.h
Documentation/driver-api/infiniband.rst-77- :internal:
Documentation/driver-api/infiniband.rst-78-
Documentation/driver-api/infiniband.rst:79:.. kernel-doc:: drivers/infiniband/ulp/iser/iscsi_iser.c
Documentation/driver-api/infiniband.rst:80: :functions: iscsi_iser_pdu_alloc iser_initialize_task_headers
Documentation/driver-api/infiniband.rst:81: iscsi_iser_task_init iscsi_iser_mtask_xmit iscsi_iser_task_xmit
Documentation/driver-api/infiniband.rst:82: iscsi_iser_cleanup_task iscsi_iser_check_protection
Documentation/driver-api/infiniband.rst:83: iscsi_iser_conn_create iscsi_iser_conn_bind
Documentation/driver-api/infiniband.rst:84: iscsi_iser_conn_start iscsi_iser_conn_stop
Documentation/driver-api/infiniband.rst:85: iscsi_iser_session_destroy iscsi_iser_session_create
Documentation/driver-api/infiniband.rst:86: iscsi_iser_set_param iscsi_iser_ep_connect iscsi_iser_ep_poll
Documentation/driver-api/infiniband.rst:87: iscsi_iser_ep_disconnect
Documentation/driver-api/infiniband.rst-88-
--
drivers/infiniband/ulp/iser/Makefile=4=ib_iser-y := iser_verbs.o iser_initiator.o iser_memory.o \
drivers/infiniband/ulp/iser/Makefile:5: iscsi_iser.o
--
drivers/infiniband/ulp/iser/iscsi_iser.c-74-
drivers/infiniband/ulp/iser/iscsi_iser.c:75:#include "iscsi_iser.h"
drivers/infiniband/ulp/iser/iscsi_iser.c-76-
--
drivers/infiniband/ulp/iser/iscsi_iser.c=79=MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
drivers/infiniband/ulp/iser/iscsi_iser.c-80-
drivers/infiniband/ulp/iser/iscsi_iser.c:81:static const struct scsi_host_template iscsi_iser_sht;
drivers/infiniband/ulp/iser/iscsi_iser.c:82:static struct iscsi_transport iscsi_iser_transport;
drivers/infiniband/ulp/iser/iscsi_iser.c:83:static struct scsi_transport_template *iscsi_iser_scsi_transport;
drivers/infiniband/ulp/iser/iscsi_iser.c-84-static struct workqueue_struct *release_wq;
--
drivers/infiniband/ulp/iser/iscsi_iser.c=90=MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)");
drivers/infiniband/ulp/iser/iscsi_iser.c-91-
drivers/infiniband/ulp/iser/iscsi_iser.c:92:static int iscsi_iser_set(const char *val, const struct kernel_param *kp);
drivers/infiniband/ulp/iser/iscsi_iser.c:93:static const struct kernel_param_ops iscsi_iser_size_ops = {
drivers/infiniband/ulp/iser/iscsi_iser.c:94: .set = iscsi_iser_set,
drivers/infiniband/ulp/iser/iscsi_iser.c-95- .get = param_get_uint,
--
drivers/infiniband/ulp/iser/iscsi_iser.c=98=static unsigned int iscsi_max_lun = 512;
drivers/infiniband/ulp/iser/iscsi_iser.c:99:module_param_cb(max_lun, &iscsi_iser_size_ops, &iscsi_max_lun, S_IRUGO);
drivers/infiniband/ulp/iser/iscsi_iser.c-100-MODULE_PARM_DESC(max_lun, "Max LUNs to allow per session, should > 0 (default:512)");
--
drivers/infiniband/ulp/iser/iscsi_iser.c=102=unsigned int iser_max_sectors = ISER_DEF_MAX_SECTORS;
drivers/infiniband/ulp/iser/iscsi_iser.c:103:module_param_cb(max_sectors, &iscsi_iser_size_ops, &iser_max_sectors,
drivers/infiniband/ulp/iser/iscsi_iser.c-104- S_IRUGO | S_IWUSR);
--
drivers/infiniband/ulp/iser/iscsi_iser.c=114=MODULE_PARM_DESC(pi_enable, "Enable T10-PI offload support (default:disabled)");
drivers/infiniband/ulp/iser/iscsi_iser.c-115-
drivers/infiniband/ulp/iser/iscsi_iser.c:116:static int iscsi_iser_set(const char *val, const struct kernel_param *kp)
drivers/infiniband/ulp/iser/iscsi_iser.c-117-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c-128-/*
drivers/infiniband/ulp/iser/iscsi_iser.c:129: * iscsi_iser_recv() - Process a successful recv completion
drivers/infiniband/ulp/iser/iscsi_iser.c-130- * @conn: iscsi connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c-137- */
drivers/infiniband/ulp/iser/iscsi_iser.c:138:void iscsi_iser_recv(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
drivers/infiniband/ulp/iser/iscsi_iser.c-139- char *rx_data, int rx_data_len)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-166-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:167: * iscsi_iser_pdu_alloc() - allocate an iscsi-iser PDU
drivers/infiniband/ulp/iser/iscsi_iser.c-168- * @task: iscsi task
--
drivers/infiniband/ulp/iser/iscsi_iser.c-173- */
drivers/infiniband/ulp/iser/iscsi_iser.c:174:static int iscsi_iser_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
drivers/infiniband/ulp/iser/iscsi_iser.c-175-{
drivers/infiniband/ulp/iser/iscsi_iser.c:176: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-177-
--
drivers/infiniband/ulp/iser/iscsi_iser.c=195=int iser_initialize_task_headers(struct iscsi_task *task,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-199- struct iser_device *device = iser_conn->ib_conn.device;
drivers/infiniband/ulp/iser/iscsi_iser.c:200: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-201- u64 dma_addr;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-224-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:225: * iscsi_iser_task_init() - Initialize iscsi-iser task
drivers/infiniband/ulp/iser/iscsi_iser.c-226- * @task: iscsi task
--
drivers/infiniband/ulp/iser/iscsi_iser.c-232- */
drivers/infiniband/ulp/iser/iscsi_iser.c:233:static int iscsi_iser_task_init(struct iscsi_task *task)
drivers/infiniband/ulp/iser/iscsi_iser.c-234-{
drivers/infiniband/ulp/iser/iscsi_iser.c:235: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-236- int ret;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-256-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:257: * iscsi_iser_mtask_xmit() - xmit management (immediate) task
drivers/infiniband/ulp/iser/iscsi_iser.c-258- * @conn: iscsi connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c-266- **/
drivers/infiniband/ulp/iser/iscsi_iser.c:267:static int iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
drivers/infiniband/ulp/iser/iscsi_iser.c-268- struct iscsi_task *task)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-280-
drivers/infiniband/ulp/iser/iscsi_iser.c:281:static int iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
drivers/infiniband/ulp/iser/iscsi_iser.c-282- struct iscsi_task *task)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-298- r2t->datasn--;
drivers/infiniband/ulp/iser/iscsi_iser.c:299: goto iscsi_iser_task_xmit_unsol_data_exit;
drivers/infiniband/ulp/iser/iscsi_iser.c-300- }
--
drivers/infiniband/ulp/iser/iscsi_iser.c-305-
drivers/infiniband/ulp/iser/iscsi_iser.c:306:iscsi_iser_task_xmit_unsol_data_exit:
drivers/infiniband/ulp/iser/iscsi_iser.c-307- return error;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-310-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:311: * iscsi_iser_task_xmit() - xmit iscsi-iser task
drivers/infiniband/ulp/iser/iscsi_iser.c-312- * @task: iscsi task
--
drivers/infiniband/ulp/iser/iscsi_iser.c-315- */
drivers/infiniband/ulp/iser/iscsi_iser.c:316:static int iscsi_iser_task_xmit(struct iscsi_task *task)
drivers/infiniband/ulp/iser/iscsi_iser.c-317-{
drivers/infiniband/ulp/iser/iscsi_iser.c-318- struct iscsi_conn *conn = task->conn;
drivers/infiniband/ulp/iser/iscsi_iser.c:319: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-320- int error = 0;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-322- if (!task->sc)
drivers/infiniband/ulp/iser/iscsi_iser.c:323: return iscsi_iser_mtask_xmit(conn, task);
drivers/infiniband/ulp/iser/iscsi_iser.c-324-
--
drivers/infiniband/ulp/iser/iscsi_iser.c-339- if (error)
drivers/infiniband/ulp/iser/iscsi_iser.c:340: goto iscsi_iser_task_xmit_exit;
drivers/infiniband/ulp/iser/iscsi_iser.c-341- iser_task->command_sent = 1;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-345- if (iscsi_task_has_unsol_data(task))
drivers/infiniband/ulp/iser/iscsi_iser.c:346: error = iscsi_iser_task_xmit_unsol_data(conn, task);
drivers/infiniband/ulp/iser/iscsi_iser.c-347-
drivers/infiniband/ulp/iser/iscsi_iser.c:348: iscsi_iser_task_xmit_exit:
drivers/infiniband/ulp/iser/iscsi_iser.c-349- return error;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-352-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:353: * iscsi_iser_cleanup_task() - cleanup an iscsi-iser task
drivers/infiniband/ulp/iser/iscsi_iser.c-354- * @task: iscsi task
--
drivers/infiniband/ulp/iser/iscsi_iser.c-359- */
drivers/infiniband/ulp/iser/iscsi_iser.c:360:static void iscsi_iser_cleanup_task(struct iscsi_task *task)
drivers/infiniband/ulp/iser/iscsi_iser.c-361-{
drivers/infiniband/ulp/iser/iscsi_iser.c:362: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-363- struct iser_tx_desc *tx_desc = &iser_task->desc;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-387-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:388: * iscsi_iser_check_protection() - check protection information status of task.
drivers/infiniband/ulp/iser/iscsi_iser.c-389- * @task: iscsi task
--
drivers/infiniband/ulp/iser/iscsi_iser.c-398- */
drivers/infiniband/ulp/iser/iscsi_iser.c:399:static u8 iscsi_iser_check_protection(struct iscsi_task *task, sector_t *sector)
drivers/infiniband/ulp/iser/iscsi_iser.c-400-{
drivers/infiniband/ulp/iser/iscsi_iser.c:401: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iscsi_iser.c-402- enum iser_data_dir dir = iser_task->dir[ISER_DIR_IN] ?
--
drivers/infiniband/ulp/iser/iscsi_iser.c-408-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:409: * iscsi_iser_conn_create() - create a new iscsi-iser connection
drivers/infiniband/ulp/iser/iscsi_iser.c-410- * @cls_session: iscsi class connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c=416=static struct iscsi_cls_conn *
drivers/infiniband/ulp/iser/iscsi_iser.c:417:iscsi_iser_conn_create(struct iscsi_cls_session *cls_session,
drivers/infiniband/ulp/iser/iscsi_iser.c-418- uint32_t conn_idx)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-437-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:438: * iscsi_iser_conn_bind() - bind iscsi and iser connection structures
drivers/infiniband/ulp/iser/iscsi_iser.c-439- * @cls_session: iscsi class session
--
drivers/infiniband/ulp/iser/iscsi_iser.c-447- */
drivers/infiniband/ulp/iser/iscsi_iser.c:448:static int iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
drivers/infiniband/ulp/iser/iscsi_iser.c-449- struct iscsi_cls_conn *cls_conn,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-497-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:498: * iscsi_iser_conn_start() - start iscsi-iser connection
drivers/infiniband/ulp/iser/iscsi_iser.c-499- * @cls_conn: iscsi class connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c-504- */
drivers/infiniband/ulp/iser/iscsi_iser.c:505:static int iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
drivers/infiniband/ulp/iser/iscsi_iser.c-506-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c-517-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:518: * iscsi_iser_conn_stop() - stop iscsi-iser connection
drivers/infiniband/ulp/iser/iscsi_iser.c-519- * @cls_conn: iscsi class connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c-526- */
drivers/infiniband/ulp/iser/iscsi_iser.c:527:static void iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
drivers/infiniband/ulp/iser/iscsi_iser.c-528-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c-556-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:557: * iscsi_iser_session_destroy() - destroy iscsi-iser session
drivers/infiniband/ulp/iser/iscsi_iser.c-558- * @cls_session: iscsi class session
--
drivers/infiniband/ulp/iser/iscsi_iser.c-561- */
drivers/infiniband/ulp/iser/iscsi_iser.c:562:static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
drivers/infiniband/ulp/iser/iscsi_iser.c-563-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c=571=static inline unsigned int iser_dif_prot_caps(int prot_caps)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-589-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:590: * iscsi_iser_session_create() - create an iscsi-iser session
drivers/infiniband/ulp/iser/iscsi_iser.c-591- * @ep: iscsi end-point handle
--
drivers/infiniband/ulp/iser/iscsi_iser.c=599=static struct iscsi_cls_session *
drivers/infiniband/ulp/iser/iscsi_iser.c:600:iscsi_iser_session_create(struct iscsi_endpoint *ep,
drivers/infiniband/ulp/iser/iscsi_iser.c-601- uint16_t cmds_max, uint16_t qdepth,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-610-
drivers/infiniband/ulp/iser/iscsi_iser.c:611: shost = iscsi_host_alloc(&iscsi_iser_sht, 0, 0);
drivers/infiniband/ulp/iser/iscsi_iser.c-612- if (!shost)
drivers/infiniband/ulp/iser/iscsi_iser.c-613- return NULL;
drivers/infiniband/ulp/iser/iscsi_iser.c:614: shost->transportt = iscsi_iser_scsi_transport;
drivers/infiniband/ulp/iser/iscsi_iser.c-615- shost->cmd_per_lun = qdepth;
--
drivers/infiniband/ulp/iser/iscsi_iser.c-673-
drivers/infiniband/ulp/iser/iscsi_iser.c:674: cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
drivers/infiniband/ulp/iser/iscsi_iser.c-675- shost->can_queue, 0,
drivers/infiniband/ulp/iser/iscsi_iser.c:676: sizeof(struct iscsi_iser_task),
drivers/infiniband/ulp/iser/iscsi_iser.c-677- initial_cmdsn, 0);
--
drivers/infiniband/ulp/iser/iscsi_iser.c-689-
drivers/infiniband/ulp/iser/iscsi_iser.c:690:static int iscsi_iser_set_param(struct iscsi_cls_conn *cls_conn,
drivers/infiniband/ulp/iser/iscsi_iser.c-691- enum iscsi_param param, char *buf, int buflen)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-734-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:735: * iscsi_iser_conn_get_stats() - get iscsi connection statistics
drivers/infiniband/ulp/iser/iscsi_iser.c-736- * @cls_conn: iscsi class connection
--
drivers/infiniband/ulp/iser/iscsi_iser.c-740- */
drivers/infiniband/ulp/iser/iscsi_iser.c:741:static void iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn,
drivers/infiniband/ulp/iser/iscsi_iser.c-742- struct iscsi_stats *stats)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-757-
drivers/infiniband/ulp/iser/iscsi_iser.c:758:static int iscsi_iser_get_ep_param(struct iscsi_endpoint *ep,
drivers/infiniband/ulp/iser/iscsi_iser.c-759- enum iscsi_param param, char *buf)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-778-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:779: * iscsi_iser_ep_connect() - Initiate iSER connection establishment
drivers/infiniband/ulp/iser/iscsi_iser.c-780- * @shost: scsi_host
--
drivers/infiniband/ulp/iser/iscsi_iser.c-792- */
drivers/infiniband/ulp/iser/iscsi_iser.c:793:static struct iscsi_endpoint *iscsi_iser_ep_connect(struct Scsi_Host *shost,
drivers/infiniband/ulp/iser/iscsi_iser.c-794- struct sockaddr *dst_addr,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-825-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:826: * iscsi_iser_ep_poll() - poll for iser connection establishment to complete
drivers/infiniband/ulp/iser/iscsi_iser.c-827- * @ep: iscsi endpoint (created at ep_connect)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-837- */
drivers/infiniband/ulp/iser/iscsi_iser.c:838:static int iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
drivers/infiniband/ulp/iser/iscsi_iser.c-839-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c-864-/**
drivers/infiniband/ulp/iser/iscsi_iser.c:865: * iscsi_iser_ep_disconnect() - Initiate connection teardown process
drivers/infiniband/ulp/iser/iscsi_iser.c-866- * @ep: iscsi endpoint handle
--
drivers/infiniband/ulp/iser/iscsi_iser.c-872- */
drivers/infiniband/ulp/iser/iscsi_iser.c:873:static void iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
drivers/infiniband/ulp/iser/iscsi_iser.c-874-{
--
drivers/infiniband/ulp/iser/iscsi_iser.c=901=static umode_t iser_attr_is_visible(int param_type, int param)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-954-
drivers/infiniband/ulp/iser/iscsi_iser.c:955:static const struct scsi_host_template iscsi_iser_sht = {
drivers/infiniband/ulp/iser/iscsi_iser.c-956- .module = THIS_MODULE,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-966- .target_alloc = iscsi_target_alloc,
drivers/infiniband/ulp/iser/iscsi_iser.c:967: .proc_name = "iscsi_iser",
drivers/infiniband/ulp/iser/iscsi_iser.c-968- .this_id = -1,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-972-
drivers/infiniband/ulp/iser/iscsi_iser.c:973:static struct iscsi_transport iscsi_iser_transport = {
drivers/infiniband/ulp/iser/iscsi_iser.c-974- .owner = THIS_MODULE,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-977- /* session management */
drivers/infiniband/ulp/iser/iscsi_iser.c:978: .create_session = iscsi_iser_session_create,
drivers/infiniband/ulp/iser/iscsi_iser.c:979: .destroy_session = iscsi_iser_session_destroy,
drivers/infiniband/ulp/iser/iscsi_iser.c-980- /* connection management */
drivers/infiniband/ulp/iser/iscsi_iser.c:981: .create_conn = iscsi_iser_conn_create,
drivers/infiniband/ulp/iser/iscsi_iser.c:982: .bind_conn = iscsi_iser_conn_bind,
drivers/infiniband/ulp/iser/iscsi_iser.c-983- .unbind_conn = iscsi_conn_unbind,
--
drivers/infiniband/ulp/iser/iscsi_iser.c-985- .attr_is_visible = iser_attr_is_visible,
drivers/infiniband/ulp/iser/iscsi_iser.c:986: .set_param = iscsi_iser_set_param,
drivers/infiniband/ulp/iser/iscsi_iser.c-987- .get_conn_param = iscsi_conn_get_param,
drivers/infiniband/ulp/iser/iscsi_iser.c:988: .get_ep_param = iscsi_iser_get_ep_param,
drivers/infiniband/ulp/iser/iscsi_iser.c-989- .get_session_param = iscsi_session_get_param,
drivers/infiniband/ulp/iser/iscsi_iser.c:990: .start_conn = iscsi_iser_conn_start,
drivers/infiniband/ulp/iser/iscsi_iser.c:991: .stop_conn = iscsi_iser_conn_stop,
drivers/infiniband/ulp/iser/iscsi_iser.c-992- /* iscsi host params */
--
drivers/infiniband/ulp/iser/iscsi_iser.c-996- .send_pdu = iscsi_conn_send_pdu,
drivers/infiniband/ulp/iser/iscsi_iser.c:997: .get_stats = iscsi_iser_conn_get_stats,
drivers/infiniband/ulp/iser/iscsi_iser.c:998: .init_task = iscsi_iser_task_init,
drivers/infiniband/ulp/iser/iscsi_iser.c:999: .xmit_task = iscsi_iser_task_xmit,
drivers/infiniband/ulp/iser/iscsi_iser.c:1000: .cleanup_task = iscsi_iser_cleanup_task,
drivers/infiniband/ulp/iser/iscsi_iser.c:1001: .alloc_pdu = iscsi_iser_pdu_alloc,
drivers/infiniband/ulp/iser/iscsi_iser.c:1002: .check_protection = iscsi_iser_check_protection,
drivers/infiniband/ulp/iser/iscsi_iser.c-1003- /* recovery */
--
drivers/infiniband/ulp/iser/iscsi_iser.c-1005-
drivers/infiniband/ulp/iser/iscsi_iser.c:1006: .ep_connect = iscsi_iser_ep_connect,
drivers/infiniband/ulp/iser/iscsi_iser.c:1007: .ep_poll = iscsi_iser_ep_poll,
drivers/infiniband/ulp/iser/iscsi_iser.c:1008: .ep_disconnect = iscsi_iser_ep_disconnect
drivers/infiniband/ulp/iser/iscsi_iser.c-1009-};
--
drivers/infiniband/ulp/iser/iscsi_iser.c=1011=static int __init iser_init(void)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-1038-
drivers/infiniband/ulp/iser/iscsi_iser.c:1039: iscsi_iser_scsi_transport = iscsi_register_transport(
drivers/infiniband/ulp/iser/iscsi_iser.c:1040: &iscsi_iser_transport);
drivers/infiniband/ulp/iser/iscsi_iser.c:1041: if (!iscsi_iser_scsi_transport) {
drivers/infiniband/ulp/iser/iscsi_iser.c-1042- iser_err("iscsi_register_transport failed\n");
--
drivers/infiniband/ulp/iser/iscsi_iser.c=1057=static void __exit iser_exit(void)
--
drivers/infiniband/ulp/iser/iscsi_iser.c-1077-
drivers/infiniband/ulp/iser/iscsi_iser.c:1078: iscsi_unregister_transport(&iscsi_iser_transport);
drivers/infiniband/ulp/iser/iscsi_iser.c-1079- kmem_cache_destroy(ig.desc_cache);
--
drivers/infiniband/ulp/iser/iscsi_iser.h=196=struct iser_device;
drivers/infiniband/ulp/iser/iscsi_iser.h:197:struct iscsi_iser_task;
drivers/infiniband/ulp/iser/iscsi_iser.h-198-struct iscsi_endpoint;
--
drivers/infiniband/ulp/iser/iscsi_iser.h=403=struct iser_conn {
--
drivers/infiniband/ulp/iser/iscsi_iser.h-425-/**
drivers/infiniband/ulp/iser/iscsi_iser.h:426: * struct iscsi_iser_task - iser task context
drivers/infiniband/ulp/iser/iscsi_iser.h-427- *
--
drivers/infiniband/ulp/iser/iscsi_iser.h-437- */
drivers/infiniband/ulp/iser/iscsi_iser.h:438:struct iscsi_iser_task {
drivers/infiniband/ulp/iser/iscsi_iser.h-439- struct iser_tx_desc desc;
--
drivers/infiniband/ulp/iser/iscsi_iser.h=479=int iser_send_data_out(struct iscsi_conn *conn,
--
drivers/infiniband/ulp/iser/iscsi_iser.h-482-
drivers/infiniband/ulp/iser/iscsi_iser.h:483:void iscsi_iser_recv(struct iscsi_conn *conn,
drivers/infiniband/ulp/iser/iscsi_iser.h-484- struct iscsi_hdr *hdr,
--
drivers/infiniband/ulp/iser/iscsi_iser.h=502=void iser_reg_comp(struct ib_cq *cq, struct ib_wc *wc);
drivers/infiniband/ulp/iser/iscsi_iser.h-503-
drivers/infiniband/ulp/iser/iscsi_iser.h:504:void iser_task_rdma_init(struct iscsi_iser_task *task);
drivers/infiniband/ulp/iser/iscsi_iser.h-505-
drivers/infiniband/ulp/iser/iscsi_iser.h:506:void iser_task_rdma_finalize(struct iscsi_iser_task *task);
drivers/infiniband/ulp/iser/iscsi_iser.h-507-
drivers/infiniband/ulp/iser/iscsi_iser.h=508=void iser_free_rx_descriptors(struct iser_conn *iser_conn);
drivers/infiniband/ulp/iser/iscsi_iser.h-509-
drivers/infiniband/ulp/iser/iscsi_iser.h:510:int iser_reg_mem_fastreg(struct iscsi_iser_task *task,
drivers/infiniband/ulp/iser/iscsi_iser.h-511- enum iser_data_dir dir,
drivers/infiniband/ulp/iser/iscsi_iser.h-512- bool all_imm);
drivers/infiniband/ulp/iser/iscsi_iser.h:513:void iser_unreg_mem_fastreg(struct iscsi_iser_task *task,
drivers/infiniband/ulp/iser/iscsi_iser.h-514- enum iser_data_dir dir);
--
drivers/infiniband/ulp/iser/iscsi_iser.h=524=int iser_post_send(struct ib_conn *ib_conn, struct iser_tx_desc *tx_desc);
drivers/infiniband/ulp/iser/iscsi_iser.h-525-
drivers/infiniband/ulp/iser/iscsi_iser.h:526:int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iscsi_iser.h-527- enum iser_data_dir iser_dir,
--
drivers/infiniband/ulp/iser/iscsi_iser.h-529-
drivers/infiniband/ulp/iser/iscsi_iser.h:530:void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iscsi_iser.h-531- enum iser_data_dir iser_dir,
--
drivers/infiniband/ulp/iser/iscsi_iser.h=541=void iser_free_fastreg_pool(struct ib_conn *ib_conn);
drivers/infiniband/ulp/iser/iscsi_iser.h:542:u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iscsi_iser.h-543- enum iser_data_dir cmd_dir, sector_t *sector);
--
drivers/infiniband/ulp/iser/iser_initiator.c-40-
drivers/infiniband/ulp/iser/iser_initiator.c:41:#include "iscsi_iser.h"
drivers/infiniband/ulp/iser/iser_initiator.c-42-
--
drivers/infiniband/ulp/iser/iser_initiator.c=48=static int iser_prepare_read_cmd(struct iscsi_task *task)
--
drivers/infiniband/ulp/iser/iser_initiator.c-50-{
drivers/infiniband/ulp/iser/iser_initiator.c:51: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-52- struct iser_mem_reg *mem_reg;
--
drivers/infiniband/ulp/iser/iser_initiator.c=89=static int iser_prepare_write_cmd(struct iscsi_task *task, unsigned int imm_sz,
--
drivers/infiniband/ulp/iser/iser_initiator.c-91-{
drivers/infiniband/ulp/iser/iser_initiator.c:92: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-93- struct iser_mem_reg *mem_reg;
--
drivers/infiniband/ulp/iser/iser_initiator.c=341=int iser_send_command(struct iscsi_conn *conn, struct iscsi_task *task)
--
drivers/infiniband/ulp/iser/iser_initiator.c-343- struct iser_conn *iser_conn = conn->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c:344: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-345- unsigned long edtl;
--
drivers/infiniband/ulp/iser/iser_initiator.c=411=int iser_send_data_out(struct iscsi_conn *conn, struct iscsi_task *task,
--
drivers/infiniband/ulp/iser/iser_initiator.c-414- struct iser_conn *iser_conn = conn->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c:415: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-416- struct iser_tx_desc *tx_desc;
--
drivers/infiniband/ulp/iser/iser_initiator.c=472=int iser_send_control(struct iscsi_conn *conn, struct iscsi_task *task)
--
drivers/infiniband/ulp/iser/iser_initiator.c-474- struct iser_conn *iser_conn = conn->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c:475: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-476- struct iser_tx_desc *mdesc = &iser_task->desc;
--
drivers/infiniband/ulp/iser/iser_initiator.c=532=void iser_login_rsp(struct ib_cq *cq, struct ib_wc *wc)
--
drivers/infiniband/ulp/iser/iser_initiator.c-560-
drivers/infiniband/ulp/iser/iser_initiator.c:561: iscsi_iser_recv(iser_conn->iscsi_conn, hdr, data, length);
drivers/infiniband/ulp/iser/iser_initiator.c-562-
--
drivers/infiniband/ulp/iser/iser_initiator.c=591=static int iser_check_remote_inv(struct iser_conn *iser_conn, struct ib_wc *wc,
--
drivers/infiniband/ulp/iser/iser_initiator.c-608- if (likely(task)) {
drivers/infiniband/ulp/iser/iser_initiator.c:609: struct iscsi_iser_task *iser_task = task->dd_data;
drivers/infiniband/ulp/iser/iser_initiator.c-610- struct iser_fr_desc *desc;
--
drivers/infiniband/ulp/iser/iser_initiator.c=633=void iser_task_rsp(struct ib_cq *cq, struct ib_wc *wc)
--
drivers/infiniband/ulp/iser/iser_initiator.c-661-
drivers/infiniband/ulp/iser/iser_initiator.c:662: iscsi_iser_recv(iser_conn->iscsi_conn, hdr, desc->data, length);
drivers/infiniband/ulp/iser/iser_initiator.c-663-
--
drivers/infiniband/ulp/iser/iser_initiator.c=695=void iser_dataout_comp(struct ib_cq *cq, struct ib_wc *wc)
--
drivers/infiniband/ulp/iser/iser_initiator.c-708-
drivers/infiniband/ulp/iser/iser_initiator.c:709:void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
drivers/infiniband/ulp/iser/iser_initiator.c-710-
--
drivers/infiniband/ulp/iser/iser_initiator.c-731-
drivers/infiniband/ulp/iser/iser_initiator.c:732:void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
drivers/infiniband/ulp/iser/iser_initiator.c-733-{
--
drivers/infiniband/ulp/iser/iser_memory.c-38-
drivers/infiniband/ulp/iser/iser_memory.c:39:#include "iscsi_iser.h"
drivers/infiniband/ulp/iser/iser_memory.c-40-
--
drivers/infiniband/ulp/iser/iser_memory.c=61=static void iser_reg_desc_put_fr(struct ib_conn *ib_conn,
--
drivers/infiniband/ulp/iser/iser_memory.c-71-
drivers/infiniband/ulp/iser/iser_memory.c:72:int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_memory.c-73- enum iser_data_dir iser_dir,
--
drivers/infiniband/ulp/iser/iser_memory.c-105-
drivers/infiniband/ulp/iser/iser_memory.c:106:void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_memory.c-107- enum iser_data_dir iser_dir,
--
drivers/infiniband/ulp/iser/iser_memory.c=123=static int iser_reg_dma(struct iser_device *device, struct iser_data_buf *mem,
--
drivers/infiniband/ulp/iser/iser_memory.c-147-
drivers/infiniband/ulp/iser/iser_memory.c:148:void iser_unreg_mem_fastreg(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_memory.c-149- enum iser_data_dir cmd_dir)
--
drivers/infiniband/ulp/iser/iser_memory.c=236=static inline void iser_inv_rkey(struct ib_send_wr *inv_wr, struct ib_mr *mr,
--
drivers/infiniband/ulp/iser/iser_memory.c-246-
drivers/infiniband/ulp/iser/iser_memory.c:247:static int iser_reg_sig_mr(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_memory.c-248- struct iser_data_buf *mem,
--
drivers/infiniband/ulp/iser/iser_memory.c-304-
drivers/infiniband/ulp/iser/iser_memory.c:305:static int iser_fast_reg_mr(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_memory.c-306- struct iser_data_buf *mem,
--
drivers/infiniband/ulp/iser/iser_memory.c-351-
drivers/infiniband/ulp/iser/iser_memory.c:352:int iser_reg_mem_fastreg(struct iscsi_iser_task *task,
drivers/infiniband/ulp/iser/iser_memory.c-353- enum iser_data_dir dir,
--
drivers/infiniband/ulp/iser/iser_verbs.c-37-
drivers/infiniband/ulp/iser/iser_verbs.c:38:#include "iscsi_iser.h"
drivers/infiniband/ulp/iser/iser_verbs.c-39-
--
drivers/infiniband/ulp/iser/iser_verbs.c=649=static void iser_cleanup_handler(struct rdma_cm_id *cma_id,
--
drivers/infiniband/ulp/iser/iser_verbs.c-664- else
drivers/infiniband/ulp/iser/iser_verbs.c:665: iser_err("iscsi_iser connection isn't bound\n");
drivers/infiniband/ulp/iser/iser_verbs.c-666- }
--
drivers/infiniband/ulp/iser/iser_verbs.c=856=int iser_post_send(struct ib_conn *ib_conn, struct iser_tx_desc *tx_desc)
--
drivers/infiniband/ulp/iser/iser_verbs.c-887-
drivers/infiniband/ulp/iser/iser_verbs.c:888:u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
drivers/infiniband/ulp/iser/iser_verbs.c-889- enum iser_data_dir cmd_dir, sector_t *sector)
]
|
| 50/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 40693
output: 25
thoughts: 66
Thoughts: **My Assessment of iSCSI Transport within User Namespaces**
Okay, so `iser`, as I recall, leverages `iscsi_transport`. My immediate question is whether or not this `iscsi_transport` is even accessible within a user namespace. Itβs a crucial architectural detail for this kind of setup. Now, iSCSI fundamentally relies on netlink, and specifically, the `NETLINK_ISCSI` family. That's the key. Therefore, the critical piece of this puzzle is determining if `NETLINK_ISCSI` is properly exposed and usable within the constraints of user namespaces. This is what I need to verify.
|
| 51/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Args: map[Expression:NETLINK_ISCSI PathPrefix:]
Results: map[Output:Documentation/userspace-api/netlink/intro.rst=82=file, they include among others - general networking (NETLINK_ROUTE),
Documentation/userspace-api/netlink/intro.rst:83:iSCSI (NETLINK_ISCSI), and audit (NETLINK_AUDIT).
Documentation/userspace-api/netlink/intro.rst-84-
--
drivers/scsi/scsi_transport_iscsi.c=4943=static __init int iscsi_transport_init(void)
--
drivers/scsi/scsi_transport_iscsi.c-4982-
drivers/scsi/scsi_transport_iscsi.c:4983: nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg);
drivers/scsi/scsi_transport_iscsi.c-4984- if (!nls) {
--
drivers/scsi/scsi_transport_iscsi.c=5039=MODULE_VERSION(ISCSI_TRANSPORT_VERSION);
drivers/scsi/scsi_transport_iscsi.c:5040:MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_ISCSI);
--
include/scsi/iscsi_if.h=318=struct iscsi_iface_param_info {
--
include/scsi/iscsi_if.h-330- * ISCSI_KEVENT_PATH_REQ is defined separately and comes after the
include/scsi/iscsi_if.h:331: * struct iscsi_uevent in the NETLINK_ISCSI message.
include/scsi/iscsi_if.h-332- */
--
include/uapi/linux/netlink.h-16-#define NETLINK_SELINUX 7 /* SELinux event notifications */
include/uapi/linux/netlink.h:17:#define NETLINK_ISCSI 8 /* Open-iSCSI */
include/uapi/linux/netlink.h-18-#define NETLINK_AUDIT 9 /* auditing */
--
security/selinux/hooks.c=1180=static inline u16 socket_type_to_security_class(int family, int type, int protocol)
--
security/selinux/hooks.c-1229- return SECCLASS_NETLINK_SELINUX_SOCKET;
security/selinux/hooks.c:1230: case NETLINK_ISCSI:
security/selinux/hooks.c:1231: return SECCLASS_NETLINK_ISCSI_SOCKET;
security/selinux/hooks.c-1232- case NETLINK_AUDIT:
--
tools/include/uapi/linux/netlink.h-16-#define NETLINK_SELINUX 7 /* SELinux event notifications */
tools/include/uapi/linux/netlink.h:17:#define NETLINK_ISCSI 8 /* Open-iSCSI */
tools/include/uapi/linux/netlink.h-18-#define NETLINK_AUDIT 9 /* auditing */
]
|
| 52/2 |
2026/08/25 01:32 |
llm |
expert |
0m
Model:
Tokens:
input: 41708
output: 32
thoughts: 214
Thoughts: **Analyzing the Netlink ISCSI Creation and Potential Bug Vector**
Okay, so the code shows `netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg);`. This immediately tells me that this Netlink socket is only created within the initial network namespace, `init_net`. That means it's *not* directly accessible from user namespaces, which have their own network namespaces. That's a pretty crucial point.
But hold on... if syzbot triggered this, and I *know* syzbot runs in a user namespace, then something isn't right. It has to be triggered via some other pathway, or the bug is in a subsystem that *is* accessible from user namespace. I need to rethink this.
Aha! `smbdirect`... that's a key! `smbdirect` is used by CIFS (SMB), right? And CIFS mounts... can unprivileged users trigger those? No, usually `mount` requires `CAP_SYS_ADMIN` in the initial namespace. Unless the filesystem is 'fs_context' aware and is designed to allow mounting in user namespaces. And CIFS is not one of those.
Wait a second, syzbot *does* have `CAP_SYS_ADMIN` in its user namespace, doesn't it? Okay, so that's the angle! Does CIFS allow mounting *within* a user namespace, even with `CAP_SYS_ADMIN`? That's the question. I need to check if CIFS has `FS_USERNS_MOUNT` set. That's the critical flag to look for to determine if it is designed to mount in user namespaces.
|
| 53/2 |
2026/08/25 01:32 |
tool |
grepper |
0m
Error:
Args: map[Expression:FS_USERNS_MOUNT PathPrefix:fs/smb/]
Results:
|
| 54/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 42325
output: 43
thoughts: 202
Thoughts: **CIFS and the Mysteries of Inactive CQEs**
Okay, so CIFS doesn't seem to support `FS_USERNS_MOUNT`, that's a key observation. But wait, could syzbot be the key here? I need to think this through. Can syzbot even *trigger* CIFS issues if it's operating in the initial namespace? That's plausible, syzbot does that sometimes if it has root, and this could be the leverage I need to untangle this.
Ah, yes, the syzbot bug report! Let me see... "smbdirect: ib_dev[syz1] removed." That's telling me `smbdirect` was definitely active at some point during the report. Now, that's interesting because `smbdirect` *shouldn't* be related to `ib_cq_pool_get`. It uses `ib_alloc_cq_any` instead, which is a different function entirely.
Right, `ib_alloc_cq_any` calls `__ib_alloc_cq`. So, does that inner function, `__ib_alloc_cq`, ultimately call `ib_cq_pool_get`? No, it shouldn't. Something is seriously amiss.
So then, why is `ib_cq_pool_cleanup` warning about `cq->cqe_used` being non-zero? That should be impossible if we're dealing with `smbdirect`'s code paths. Something is getting very mixed up here. I need to go back and examine `ib_cq_pool_cleanup` again very carefully, and see if I'm missing something, because there's clearly a missing piece in this puzzle.
|
| 55/2 |
2026/08/25 01:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_cq_pool_cleanup]
Results: map[SourceCode: 362: void ib_cq_pool_cleanup(struct ib_device *dev)
363: {
364: struct ib_cq *cq, *n;
365: unsigned int i;
366:
367: for (i = 0; i < ARRAY_SIZE(dev->cq_pools); i++) {
368: list_for_each_entry_safe(cq, n, &dev->cq_pools[i],
369: pool_entry) {
370: WARN_ON(cq->cqe_used);
371: list_del(&cq->pool_entry);
372: cq->shared = false;
373: ib_free_cq(cq);
374: }
375: }
376: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 56/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 43199
output: 31
thoughts: 94
Thoughts: **Analyzing a Potential CQ Pool Issue**
Okay, let me think this through. I'm looking at this `dev->cq_pools` structure, and it's an array of lists. My initial thought is, could `smbdirect` be the culprit here, somehow adding its completion queue to these pools incorrectly? But no, hang on. I remember that `smbdirect` uses `ib_alloc_cq_any`, and that function is designed *not* to manipulate the `cq_pools` array. So, that's a dead end.
Now, I need to broaden my scope. If it's not `smbdirect`, is there *any* other ULP (Upper Layer Protocol) that's using `ib_cq_pool_get`? We need to figure out what else could be messing with those pools. What about `rds`? That's a good candidate to investigate next. Let's see if that might be it.
|
| 57/2 |
2026/08/25 01:33 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/rds/]
Results:
|
| 58/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 43544
output: 38
thoughts: 8
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Initial RPC Considerations for RDMA**
Okay, so we're talking about high-performance networking, and immediately my mind jumps to RDMA. But the question is, how do we *use* RDMA? How do we interface with it for actual remote procedure calls? We need a robust mechanism, and that's where I start thinking, "What about `rpcrdma`?" I wonder about its maturity, its performance characteristics in the real world, its compatibility with existing systems... it seems like the most obvious starting point, but I need to quickly assess if it's even a viable option. What are the trade-offs? Does it have any significant drawbacks? Is it battle-tested? This really is the pivot point. Let's see...
|
| 59/2 |
2026/08/25 01:33 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/sunrpc/xprtrdma/]
Results:
|
| 60/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 43789
output: 32
thoughts: 7
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `smc` as a Potential Solution**
Alright, so I'm thinking about this problem, and my initial approach isn't quite clicking. My mind immediately jumps to a potential avenue: `smc`. I need to start assessing if it's applicable here. Is it robust enough? Is there any potential for an improvement by investigating that tool? Iβm envisioning what it can do and trying to see if that fits within the framework of this project. Ultimately, that's what I'm asking myself here, because the tool could provide the perfect solution!
|
| 61/2 |
2026/08/25 01:33 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:net/smc/]
Results:
|
| 62/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 43990
output: 43
thoughts: 260
Thoughts: **My Initial Thoughts on the `ib_cq_pool_get` Issue**
Okay, so I've already done a global grep for `ib_cq_pool_get`, and it seems like the only users are `iser`, `isert`, `rtrs`, `srpt`, `nvme-rdma`, and `nvmet-rdma`. Thatβs the starting point. The problem is, if none of those are directly accessible from the "outside," how did syzbot even manage to trigger this `ib_cq_pool_get` issue?
My first guess is that it *somehow* involved `nvme-rdma`, perhaps? But I can't immediately see how syzbot would configure it because of the `/dev/nvme-fabrics` dependency. Then again, could syzbot somehow be interacting with `rtrs`? Remember `rtrs` uses sysfs, that's possible. Hmm, wait, maybe `nvmet-rdma` is the culprit, using configfs. Syzbot *does* have some level of configfs support, so thatβs a real possibility.
If syzbot *did* trigger it through one of these ULPs, it strongly suggests a bug within the ULP itself, where it's leaking Completion Queue Entries (CQEs). I need to check if there are any known issues in those ULPβs regarding `ib_cq_pool_put`.
Wait a minute, what if the problem isn't in those ULPs at all? What if the bug is actually within `ib_cq_pool_get` or `ib_cq_pool_put` themselves? Need to consider that. Ok, letβs start by taking a closer look at `ib_cq_pool_get`.
|
| 63/2 |
2026/08/25 01:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_cq_pool_get]
Results: map[SourceCode: 422: /**
423: * ib_cq_pool_get() - Find the least used completion queue that matches
424: * a given cpu hint (or least used for wild card affinity) and fits
425: * nr_cqe.
426: * @dev: rdma device
427: * @nr_cqe: number of needed cqe entries
428: * @comp_vector_hint: completion vector hint (-1) for the driver to assign
429: * a comp vector based on internal counter
430: * @poll_ctx: cq polling context
431: *
432: * Finds a cq that satisfies @comp_vector_hint and @nr_cqe requirements and
433: * claim entries in it for us. In case there is no available cq, allocate
434: * a new cq with the requirements and add it to the device pool.
435: * IB_POLL_DIRECT cannot be used for shared cqs so it is not a valid value
436: * for @poll_ctx.
437: */
438: struct ib_cq *ib_cq_pool_get(struct ib_device *dev, unsigned int nr_cqe,
439: int comp_vector_hint,
440: enum ib_poll_context poll_ctx)
441: {
442: static unsigned int default_comp_vector;
443: unsigned int vector, num_comp_vectors;
444: struct ib_cq *cq, *found = NULL;
445: int ret;
446:
447: if (poll_ctx > IB_POLL_LAST_POOL_TYPE) {
448: WARN_ON_ONCE(poll_ctx > IB_POLL_LAST_POOL_TYPE);
449: return ERR_PTR(-EINVAL);
450: }
451:
452: num_comp_vectors =
453: min_t(unsigned int, dev->num_comp_vectors, num_online_cpus());
454: /* Project the affinty to the device completion vector range */
455: if (comp_vector_hint < 0) {
456: comp_vector_hint =
457: (READ_ONCE(default_comp_vector) + 1) % num_comp_vectors;
458: WRITE_ONCE(default_comp_vector, comp_vector_hint);
459: }
460: vector = comp_vector_hint % num_comp_vectors;
461:
462: /*
463: * Find the least used CQ with correct affinity and
464: * enough free CQ entries
465: */
466: while (!found) {
467: spin_lock_irq(&dev->cq_pools_lock);
468: list_for_each_entry(cq, &dev->cq_pools[poll_ctx],
469: pool_entry) {
470: /*
471: * Check to see if we have found a CQ with the
472: * correct completion vector
473: */
474: if (vector != cq->comp_vector)
475: continue;
476: if (cq->cqe_used + nr_cqe > cq->cqe)
477: continue;
478: found = cq;
479: break;
480: }
481:
482: if (found) {
483: found->cqe_used += nr_cqe;
484: spin_unlock_irq(&dev->cq_pools_lock);
485:
486: return found;
487: }
488: spin_unlock_irq(&dev->cq_pools_lock);
489:
490: /*
491: * Didn't find a match or ran out of CQs in the device
492: * pool, allocate a new array of CQs.
493: */
494: ret = ib_alloc_cqs(dev, nr_cqe, poll_ctx);
495: if (ret)
496: return ERR_PTR(ret);
497: }
498:
499: return found;
500: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 64/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 45927
output: 25
thoughts: 994
Thoughts: **Analyzing a Potential Infinite Loop in IB CQ Management**
Okay, let's break this down. My initial thought is about the logic of `ib_cq_pool_get`. It appears to be designed to retrieve a completion queue (CQ) from a pool, but I'm seeing a potential flaw in how it handles CQ allocation when the requested number of completion queue entries (`nr_cqe`) exceeds the available CQ capacity.
The code seems to use `ib_alloc_cqs` to allocate new CQs if the pool doesn't have a suitable one. But `ib_alloc_cqs` calls `ib_alloc_cq`, and, crucially, the size of newly allocated CQs is limited by `dev->attrs.max_cqe`. If `nr_cqe` requested by `ib_cq_pool_get` *is* larger than `dev->attrs.max_cqe`, the allocated CQ will *always* be too small because it calls `min(dev->attrs.max_cqe, max(nr_cqes, IB_MAX_SHARED_CQ_SZ));`.
The loop in `ib_cq_pool_get` would keep re-checking the same condition: `cq->cqe_used + nr_cqe > cq->cqe`. Since the newly allocated CQ's size (`cq->cqe`) would be capped at `dev->attrs.max_cqe` while `nr_cqe` remains larger, the condition would *always* be true, leading to an infinite loop, continuously calling `ib_alloc_cqs`.
Now, if `ib_alloc_cq` actually returned an error on memory exhaustion, `ib_cq_pool_get` *would* eventually fail, preventing an infinite loop. However, the report shows that the error manifests as a warning in `ib_cq_pool_cleanup`, which doesn't seem to indicate an OOM or soft lockup. This suggests that the code *did* manage to get a CQ from the pool, meaning that at some point, `nr_cqe` was less than or equal to `cq->cqe`.
The warning arises because the `cq->cqe_used` is not zero, implying that completion queue entries *were* used, but no call to `ib_cq_pool_put` happened, the ULP missed the call.
I have to wonder where this function might be used. I checked `fs/smb` since that's often a source of RDMA activity. I found that `ib_cq_pool_get` is not used in that context. In fact, `smbdirect` is calling `ib_alloc_cq_any`, which doesn't seem to interact with the CQ pool. I've confirmed that `cqe_used` is only modified by `ib_cq_pool_get` and `ib_cq_pool_put` in the code. Therefore, somebody *must* be using `ib_cq_pool_get`.
The bug report says `"smbdirect: ib_dev[syz1] removed"`. This message is printed by `smbdirect_ib_client_remove`. The removal code, when the device is unregistered, removes clients in LIFO order. Clients are registered with `ib_register_client`.
|
| 65/2 |
2026/08/25 01:33 |
tool |
grepper |
0m
Args: map[Expression:ib_register_client PathPrefix:]
Results: map[Output:drivers/infiniband/core/cm.c=4506=static int __init ib_cm_init(void)
--
drivers/infiniband/core/cm.c-4527-
drivers/infiniband/core/cm.c:4528: ret = ib_register_client(&cm_client);
drivers/infiniband/core/cm.c-4529- if (ret)
--
drivers/infiniband/core/cma.c=5488=static int __init cma_init(void)
--
drivers/infiniband/core/cma.c-5516-
drivers/infiniband/core/cma.c:5517: ret = ib_register_client(&cma_client);
drivers/infiniband/core/cma.c-5518- if (ret)
--
drivers/infiniband/core/device.c=1986=static void remove_client_id(struct ib_client *client)
--
drivers/infiniband/core/device.c-1996-/**
drivers/infiniband/core/device.c:1997: * ib_register_client - Register an IB client
drivers/infiniband/core/device.c-1998- * @client:Client to register
drivers/infiniband/core/device.c-1999- *
drivers/infiniband/core/device.c:2000: * Upper level users of the IB drivers can use ib_register_client() to
drivers/infiniband/core/device.c-2001- * register callbacks for IB device addition and removal. When an IB
--
drivers/infiniband/core/device.c-2005- * order that clients were registered). In addition, when
drivers/infiniband/core/device.c:2006: * ib_register_client() is called, the client will receive an add
drivers/infiniband/core/device.c-2007- * callback for all devices already registered.
drivers/infiniband/core/device.c-2008- */
drivers/infiniband/core/device.c:2009:int ib_register_client(struct ib_client *client)
drivers/infiniband/core/device.c-2010-{
--
drivers/infiniband/core/device.c-2042-}
drivers/infiniband/core/device.c:2043:EXPORT_SYMBOL(ib_register_client);
drivers/infiniband/core/device.c-2044-
--
drivers/infiniband/core/mad.c=3427=int ib_mad_init(void)
--
drivers/infiniband/core/mad.c-3436-
drivers/infiniband/core/mad.c:3437: if (ib_register_client(&mad_client)) {
drivers/infiniband/core/mad.c-3438- pr_err("Couldn't register ib_mad client\n");
--
drivers/infiniband/core/multicast.c=879=int mcast_init(void)
--
drivers/infiniband/core/multicast.c-888-
drivers/infiniband/core/multicast.c:889: ret = ib_register_client(&mcast_client);
drivers/infiniband/core/multicast.c-890- if (ret)
--
drivers/infiniband/core/sa_query.c=2507=int ib_sa_init(void)
--
drivers/infiniband/core/sa_query.c-2514-
drivers/infiniband/core/sa_query.c:2515: ret = ib_register_client(&sa_client);
drivers/infiniband/core/sa_query.c-2516- if (ret) {
--
drivers/infiniband/core/ucma.c=1975=static int __init ucma_init(void)
--
drivers/infiniband/core/ucma.c-1995-
drivers/infiniband/core/ucma.c:1996: ret = ib_register_client(&rdma_cma_client);
drivers/infiniband/core/ucma.c-1997- if (ret)
--
drivers/infiniband/core/user_mad.c=1452=static int __init ib_umad_init(void)
--
drivers/infiniband/core/user_mad.c-1478-
drivers/infiniband/core/user_mad.c:1479: ret = ib_register_client(&umad_client);
drivers/infiniband/core/user_mad.c-1480- if (ret)
--
drivers/infiniband/core/user_mad.c-1482-
drivers/infiniband/core/user_mad.c:1483: ret = ib_register_client(&issm_client);
drivers/infiniband/core/user_mad.c-1484- if (ret)
--
drivers/infiniband/core/uverbs_main.c=1291=static int __init ib_uverbs_init(void)
--
drivers/infiniband/core/uverbs_main.c-1322-
drivers/infiniband/core/uverbs_main.c:1323: ret = ib_register_client(&uverbs_client);
drivers/infiniband/core/uverbs_main.c-1324- if (ret) {
--
drivers/infiniband/ulp/ipoib/ipoib_main.c=2725=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2766-
drivers/infiniband/ulp/ipoib/ipoib_main.c:2767: ret = ib_register_client(&ipoib_client);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2768- if (ret)
--
drivers/infiniband/ulp/rtrs/rtrs-srv.c=2254=struct rtrs_srv_ctx *rtrs_srv_open(struct rtrs_srv_ops *ops, u16 port)
--
drivers/infiniband/ulp/rtrs/rtrs-srv.c-2266-
drivers/infiniband/ulp/rtrs/rtrs-srv.c:2267: err = ib_register_client(&rtrs_srv_client);
drivers/infiniband/ulp/rtrs/rtrs-srv.c-2268- if (err) {
--
drivers/infiniband/ulp/srp/ib_srp.c=4177=static int __init srp_init_module(void)
--
drivers/infiniband/ulp/srp/ib_srp.c-4236-
drivers/infiniband/ulp/srp/ib_srp.c:4237: ret = ib_register_client(&srp_client);
drivers/infiniband/ulp/srp/ib_srp.c-4238- if (ret) {
--
drivers/infiniband/ulp/srpt/ib_srpt.c=3903=static const struct target_core_fabric_ops srpt_template = {
--
drivers/infiniband/ulp/srpt/ib_srpt.c-3942- *
drivers/infiniband/ulp/srpt/ib_srpt.c:3943: * Note: Since ib_register_client() registers callback functions, and since at
drivers/infiniband/ulp/srpt/ib_srpt.c-3944- * least one of these callback functions (srpt_add_one()) calls target core
drivers/infiniband/ulp/srpt/ib_srpt.c-3945- * functions, this driver must be registered with the target core before
drivers/infiniband/ulp/srpt/ib_srpt.c:3946: * ib_register_client() is called.
drivers/infiniband/ulp/srpt/ib_srpt.c-3947- */
drivers/infiniband/ulp/srpt/ib_srpt.c=3948=static int __init srpt_init_module(void)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-3969-
drivers/infiniband/ulp/srpt/ib_srpt.c:3970: ret = ib_register_client(&srpt_client);
drivers/infiniband/ulp/srpt/ib_srpt.c-3971- if (ret) {
--
drivers/nvme/host/rdma.c=2443=static int __init nvme_rdma_init_module(void)
--
drivers/nvme/host/rdma.c-2446-
drivers/nvme/host/rdma.c:2447: ret = ib_register_client(&nvme_rdma_ib_client);
drivers/nvme/host/rdma.c-2448- if (ret)
--
drivers/nvme/target/rdma.c=2131=static int __init nvmet_rdma_init(void)
--
drivers/nvme/target/rdma.c-2134-
drivers/nvme/target/rdma.c:2135: ret = ib_register_client(&nvmet_rdma_ib_client);
drivers/nvme/target/rdma.c-2136- if (ret)
--
fs/smb/smbdirect/devices.c=243=__init int smbdirect_devices_init(void)
--
fs/smb/smbdirect/devices.c-249-
fs/smb/smbdirect/devices.c:250: ret = ib_register_client(&smbdirect_ib_client);
fs/smb/smbdirect/devices.c-251- if (ret) {
fs/smb/smbdirect/devices.c:252: pr_crit("failed to ib_register_client: %d %1pe\n",
fs/smb/smbdirect/devices.c-253- ret, SMBDIRECT_DEBUG_ERR_PTR(ret));
--
include/rdma/ib_verbs.h=3136=void ib_unregister_device_queued(struct ib_device *ib_dev);
include/rdma/ib_verbs.h-3137-
include/rdma/ib_verbs.h:3138:int ib_register_client (struct ib_client *client);
include/rdma/ib_verbs.h-3139-void ib_unregister_client(struct ib_client *client);
--
net/rds/ib.c=588=int rds_ib_init(void)
--
net/rds/ib.c-597-
net/rds/ib.c:598: ret = ib_register_client(&rds_ib_client);
net/rds/ib.c-599- if (ret)
--
net/smc/af_smc.c=3450=static int __init smc_init(void)
--
net/smc/af_smc.c-3524-
net/smc/af_smc.c:3525: rc = smc_ib_register_client();
net/smc/af_smc.c-3526- if (rc) {
--
net/smc/smc_ib.c=911=void smc_ib_ndev_change(struct net_device *ndev, unsigned long event)
--
net/smc/smc_ib.c-936-
net/smc/smc_ib.c:937:/* callback function for ib_register_client() */
net/smc/smc_ib.c-938-static int smc_ib_add_dev(struct ib_device *ibdev)
--
net/smc/smc_ib.c=1010=static struct ib_client smc_ib_client = {
--
net/smc/smc_ib.c-1015-
net/smc/smc_ib.c:1016:int __init smc_ib_register_client(void)
net/smc/smc_ib.c-1017-{
net/smc/smc_ib.c-1018- smc_ib_init_local_systemid();
net/smc/smc_ib.c:1019: return ib_register_client(&smc_ib_client);
net/smc/smc_ib.c-1020-}
--
net/smc/smc_ib.h=83=void smc_ib_ndev_change(struct net_device *ndev, unsigned long event);
net/smc/smc_ib.h:84:int smc_ib_register_client(void) __init;
net/smc/smc_ib.h-85-void smc_ib_unregister_client(void);
--
net/sunrpc/xprtrdma/ib_client.c=215=int rpcrdma_ib_client_register(void)
net/sunrpc/xprtrdma/ib_client.c-216-{
net/sunrpc/xprtrdma/ib_client.c:217: return ib_register_client(&rpcrdma_ib_client);
net/sunrpc/xprtrdma/ib_client.c-218-}
]
|
| 66/2 |
2026/08/25 01:33 |
llm |
expert |
0m
Model:
Tokens:
input: 51190
output: 43
thoughts: 293
Thoughts: **Analyzing a Potential RDMA CQE Leak**
Alright, let's break this down. So, `smbdirect` is just *one* potential consumer of the RDMA infrastructure. The `disable_device` function removes clients, and it seems to be using a Last-In, First-Out (LIFO) approach. If `smbdirect` was the last client removed (or one of the last), we're seeing this specific print message: "`smbdirect: ib_dev[syz1] removed`." Following that, `ib_cq_pool_cleanup` is executed. The key here is the warning: "SOME client leaked CQEs." That's the problem.
Now, which client is leaking? We've got a range of possibilities: `nvme-rdma`, `nvmet-rdma`, `iser`, `isert`, `rtrs`, `srpt`. My initial thought was that perhaps syzbot triggered `nvme-rdma` via netlink, which would be an easy test case; however, `nvme-rdma` uses `/dev/nvme-fabrics`, so that seems unlikely. Okay, what about `rtrs`? `rtrs` uses sysfs, so that is another possibility. Perhaps `iser`? `iser` uses netlink, but `NETLINK_ISCSI` is only in `init_net`. Hold on... syzbot *can* sometimes run in `init_net` if it has root! That opens up `iser` as a likely option.
But, wait... maybe we're looking in the wrong place. Is the problem even in one of these ULPs? What if the bug isn't in a client, but in `ib_cq_pool_get` itself? That function is the gateway, so let's revisit that code path. We need to go back and carefully analyze `ib_cq_pool_get`.
|
| 67/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_cq_pool_get]
Results: map[SourceCode: 422: /**
423: * ib_cq_pool_get() - Find the least used completion queue that matches
424: * a given cpu hint (or least used for wild card affinity) and fits
425: * nr_cqe.
426: * @dev: rdma device
427: * @nr_cqe: number of needed cqe entries
428: * @comp_vector_hint: completion vector hint (-1) for the driver to assign
429: * a comp vector based on internal counter
430: * @poll_ctx: cq polling context
431: *
432: * Finds a cq that satisfies @comp_vector_hint and @nr_cqe requirements and
433: * claim entries in it for us. In case there is no available cq, allocate
434: * a new cq with the requirements and add it to the device pool.
435: * IB_POLL_DIRECT cannot be used for shared cqs so it is not a valid value
436: * for @poll_ctx.
437: */
438: struct ib_cq *ib_cq_pool_get(struct ib_device *dev, unsigned int nr_cqe,
439: int comp_vector_hint,
440: enum ib_poll_context poll_ctx)
441: {
442: static unsigned int default_comp_vector;
443: unsigned int vector, num_comp_vectors;
444: struct ib_cq *cq, *found = NULL;
445: int ret;
446:
447: if (poll_ctx > IB_POLL_LAST_POOL_TYPE) {
448: WARN_ON_ONCE(poll_ctx > IB_POLL_LAST_POOL_TYPE);
449: return ERR_PTR(-EINVAL);
450: }
451:
452: num_comp_vectors =
453: min_t(unsigned int, dev->num_comp_vectors, num_online_cpus());
454: /* Project the affinty to the device completion vector range */
455: if (comp_vector_hint < 0) {
456: comp_vector_hint =
457: (READ_ONCE(default_comp_vector) + 1) % num_comp_vectors;
458: WRITE_ONCE(default_comp_vector, comp_vector_hint);
459: }
460: vector = comp_vector_hint % num_comp_vectors;
461:
462: /*
463: * Find the least used CQ with correct affinity and
464: * enough free CQ entries
465: */
466: while (!found) {
467: spin_lock_irq(&dev->cq_pools_lock);
468: list_for_each_entry(cq, &dev->cq_pools[poll_ctx],
469: pool_entry) {
470: /*
471: * Check to see if we have found a CQ with the
472: * correct completion vector
473: */
474: if (vector != cq->comp_vector)
475: continue;
476: if (cq->cqe_used + nr_cqe > cq->cqe)
477: continue;
478: found = cq;
479: break;
480: }
481:
482: if (found) {
483: found->cqe_used += nr_cqe;
484: spin_unlock_irq(&dev->cq_pools_lock);
485:
486: return found;
487: }
488: spin_unlock_irq(&dev->cq_pools_lock);
489:
490: /*
491: * Didn't find a match or ran out of CQs in the device
492: * pool, allocate a new array of CQs.
493: */
494: ret = ib_alloc_cqs(dev, nr_cqe, poll_ctx);
495: if (ret)
496: return ERR_PTR(ret);
497: }
498:
499: return found;
500: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 68/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 53192
output: 44
thoughts: 81
Thoughts: **Analyzing CQ Pool Usage in NVMe/RDMA Driver**
Okay, hold on a second. I need to make sure I'm understanding this `ib_cq_pool_get` function correctly. It looks like it increments `cqe_used` by `nr_cqe`. That's important. Now, if the code calling `ib_cq_pool_get` runs into a snagβlike it can't allocate some other resource it needs and has to bail out with an errorβit *absolutely* has to call `ib_cq_pool_put` to free up that context. Otherwise, we'll have a leak, and that's never good. Let's see how this is implemented. I should double check `nvme_rdma_create_cq` in `drivers/nvme/host/rdma.c` to see if it correctly handles that error condition and cleans up if needed. I really need to confirm the error paths are properly handled here.
|
| 69/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_create_cq]
Results: map[SourceCode: 472: static int nvme_rdma_create_cq(struct ib_device *ibdev,
473: struct nvme_rdma_queue *queue)
474: {
475: int ret, comp_vector, idx = nvme_rdma_queue_idx(queue);
476:
477: /*
478: * Spread I/O queues completion vectors according their queue index.
479: * Admin queues can always go on completion vector 0.
480: */
481: comp_vector = (idx == 0 ? idx : idx - 1) % ibdev->num_comp_vectors;
482:
483: /* Polling queues need direct cq polling context */
484: if (nvme_rdma_poll_queue(queue))
485: queue->ib_cq = ib_alloc_cq(ibdev, queue, queue->cq_size,
486: comp_vector, IB_POLL_DIRECT);
487: else
488: queue->ib_cq = ib_cq_pool_get(ibdev, queue->cq_size,
489: comp_vector, IB_POLL_SOFTIRQ);
490:
491: if (IS_ERR(queue->ib_cq)) {
492: ret = PTR_ERR(queue->ib_cq);
493: return ret;
494: }
495:
496: return 0;
497: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 70/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 53981
output: 44
thoughts: 15
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `nvme_rdma_alloc_queue`**
Okay, time to get into the weeds. Let's see... the task at hand is `nvme_rdma_alloc_queue`. I need to understand exactly how this function works within the larger NVMe-over-RDMA context. Clearly, it's responsible for allocating and initializing an I/O queue for an NVMe device when using RDMA.
I'll be looking for things like: what memory allocations are happening? Are there any specific RDMA-related resources being set up (QP creation, memory registration, CQ/SQ initialization)? How is it interacting with the NVMe driver core? Are there any potential areas for optimization, or common pitfalls that I should keep in mind during code reviews? Whatβs the expected lifecycle of this queue after allocation β how is it later freed? I need to build a comprehensive mental model here.
|
| 71/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_alloc_queue]
Results: map[SourceCode: 579: static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
580: {
581: struct nvme_rdma_ctrl *ctrl = queue->ctrl;
582: int idx = nvme_rdma_queue_idx(queue);
583: struct sockaddr *src_addr = NULL;
584: int ret;
585:
586: mutex_init(&queue->queue_lock);
587: if (idx && ctrl->ctrl.max_integrity_segments)
588: queue->pi_support = true;
589: else
590: queue->pi_support = false;
591: init_completion(&queue->cm_done);
592:
593: if (idx > 0)
594: queue->cmnd_capsule_len = ctrl->ctrl.ioccsz * 16;
595: else
596: queue->cmnd_capsule_len = sizeof(struct nvme_command);
597:
598: queue->cm_id = rdma_create_id(&init_net, nvme_rdma_cm_handler, queue,
599: RDMA_PS_TCP, IB_QPT_RC);
600: if (IS_ERR(queue->cm_id)) {
601: dev_info(ctrl->ctrl.device,
602: "failed to create CM ID: %ld\n", PTR_ERR(queue->cm_id));
603: ret = PTR_ERR(queue->cm_id);
604: goto out_destroy_mutex;
605: }
606:
607: if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR)
608: src_addr = (struct sockaddr *)&ctrl->src_addr;
609:
610: queue->cm_error = -ETIMEDOUT;
611: ret = rdma_resolve_addr(queue->cm_id, src_addr,
612: (struct sockaddr *)&ctrl->addr,
613: NVME_RDMA_CM_TIMEOUT_MS);
614: if (ret) {
615: dev_info(ctrl->ctrl.device,
616: "rdma_resolve_addr failed (%d).\n", ret);
617: goto out_destroy_cm_id;
618: }
619:
620: ret = nvme_rdma_wait_for_cm(queue);
621: if (ret) {
622: dev_info(ctrl->ctrl.device,
623: "rdma connection establishment failed (%d)\n", ret);
624: goto out_destroy_cm_id;
625: }
626:
627: set_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags);
628:
629: return 0;
630:
631: out_destroy_cm_id:
632: rdma_destroy_id(queue->cm_id);
633: nvme_rdma_destroy_queue_ib(queue);
634: out_destroy_mutex:
635: mutex_destroy(&queue->queue_lock);
636: return ret;
637: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 72/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 55226
output: 57
thoughts: 44
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as someone deeply familiar with this code:
**Where's the CQ Creation? A Quick Search Required**
Right, let me think this through... I'm looking at `nvme_rdma_alloc_queue`, and it's hitting me: this function *doesn't* seem to be the one that actually calls `nvme_rdma_create_cq`. That's a bit odd. I was sure that was where it lived.
So, the obvious next step is to figure out *where* that crucial `nvme_rdma_create_cq` function *is* being called. I need to trace the execution path. Time to fire up the tools and search through the code to find its true origin. I suspect I might have been looking at an older version of the code, or possibly I'm just getting my calls mixed up. Let me dig in and get to the bottom of this.
|
| 73/2 |
2026/08/25 01:34 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c IncludeSnippetLines:0 Name:nvme_rdma_create_cq SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvme_rdma_create_queue_ib SourceFile:drivers/nvme/host/rdma.c SourceLine:517]] TruncatedOutput:false]
|
| 74/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 55616
output: 46
thoughts: 17
Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were a seasoned professional in the field, speaking directly to another expert:
**Analyzing `nvme_rdma_create_queue_ib`**
Alright, let's dive into `nvme_rdma_create_queue_ib`. I want to quickly refresh my understanding of what this function *actually* does. It's the core of the RDMA queue creation, so it's critical to performance. I'm immediately looking for how it interacts with the underlying IB verbs β likely allocating and initializing resources on the HCA. Things like work queues, completion queues, memory registrationβ¦ all of that needs to be properly set up here. I'll need to pay close attention to the error handling within this function β `nvme` can be tricky to debug at times, so I'm thinking, "Is there a retry mechanism?" Does it roll back the changes, or will this have to be handled higher up the call stack. Let's see how they handle resource cleanup on error paths, and what kind of checks are made to see if we even *need* to do this or not. Are they caching or pre-creating queues, or is it a truly on-demand allocation? That's going to affect overhead, especially during I/O spikes. Furthermore, I'll need to confirm what the queue depth parameters passed to this function translate into on the hardware, and if there are any driver-specific optimizations or limitations. I'm going to quickly skim the surrounding code for potential race conditions or locking issues, especially if this function is called from a multi-threaded context. And I'm looking for how this function gets the appropriate hardware context -- are they using an existing fabric, or calling out to manage the new fabric?
|
| 75/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_create_queue_ib]
Results: map[SourceCode: 499: static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue)
500: {
501: struct ib_device *ibdev;
502: const int send_wr_factor = 3; /* MR, SEND, INV */
503: const int cq_factor = send_wr_factor + 1; /* + RECV */
504: int ret, pages_per_mr;
505:
506: queue->device = nvme_rdma_find_get_device(queue->cm_id);
507: if (!queue->device) {
508: dev_err(queue->cm_id->device->dev.parent,
509: "no client data found!\n");
510: return -ECONNREFUSED;
511: }
512: ibdev = queue->device->dev;
513:
514: /* +1 for ib_drain_qp */
515: queue->cq_size = cq_factor * queue->queue_size + 1;
516:
517: ret = nvme_rdma_create_cq(ibdev, queue);
518: if (ret)
519: goto out_put_dev;
520:
521: ret = nvme_rdma_create_qp(queue, send_wr_factor);
522: if (ret)
523: goto out_destroy_ib_cq;
524:
525: queue->rsp_ring = nvme_rdma_alloc_ring(ibdev, queue->queue_size,
526: sizeof(struct nvme_completion), DMA_FROM_DEVICE);
527: if (!queue->rsp_ring) {
528: ret = -ENOMEM;
529: goto out_destroy_qp;
530: }
531:
532: /*
533: * Currently we don't use SG_GAPS MR's so if the first entry is
534: * misaligned we'll end up using two entries for a single data page,
535: * so one additional entry is required.
536: */
537: pages_per_mr = nvme_rdma_get_max_fr_pages(ibdev, queue->pi_support) + 1;
538: ret = ib_mr_pool_init(queue->qp, &queue->qp->rdma_mrs,
539: queue->queue_size,
540: IB_MR_TYPE_MEM_REG,
541: pages_per_mr, 0);
542: if (ret) {
543: dev_err(queue->ctrl->ctrl.device,
544: "failed to initialize MR pool sized %d for QID %d\n",
545: queue->queue_size, nvme_rdma_queue_idx(queue));
546: goto out_destroy_ring;
547: }
548:
549: if (queue->pi_support) {
550: ret = ib_mr_pool_init(queue->qp, &queue->qp->sig_mrs,
551: queue->queue_size, IB_MR_TYPE_INTEGRITY,
552: pages_per_mr, pages_per_mr);
553: if (ret) {
554: dev_err(queue->ctrl->ctrl.device,
555: "failed to initialize PI MR pool sized %d for QID %d\n",
556: queue->queue_size, nvme_rdma_queue_idx(queue));
557: goto out_destroy_mr_pool;
558: }
559: }
560:
561: set_bit(NVME_RDMA_Q_TR_READY, &queue->flags);
562:
563: return 0;
564:
565: out_destroy_mr_pool:
566: ib_mr_pool_destroy(queue->qp, &queue->qp->rdma_mrs);
567: out_destroy_ring:
568: nvme_rdma_free_ring(ibdev, queue->rsp_ring, queue->queue_size,
569: sizeof(struct nvme_completion), DMA_FROM_DEVICE);
570: out_destroy_qp:
571: rdma_destroy_qp(queue->cm_id);
572: out_destroy_ib_cq:
573: nvme_rdma_free_cq(queue);
574: out_put_dev:
575: nvme_rdma_dev_put(queue->device);
576: return ret;
577: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 76/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 57435
output: 44
thoughts: 36
Thoughts: Here's my take on the thought process, as if it were my own:
**Investigating a Potential NVMe-RDMA Issue**
Okay, hold on a second. I'm seeing `nvme_rdma_free_cq` being called within an error path. That's immediately a red flag. We know how crucial the completion queue (CQ) is for NVMe operations, and freeing it prematurely or in the wrong context is likely to cause some serious problems, like data corruption or system crashes. Given my experience with NVMe and RDMA integrations, I need to understand *why* this call is happening in the error path. So, let's dive into the `nvme_rdma_free_cq` function itself. I need to understand its entry conditions, any pre-requisites, and how it is expected to be used. I need to confirm if its being called in the right order. From there, I can determine why its being called when there is an error.
|
| 77/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_free_cq]
Results: map[SourceCode: 423: static void nvme_rdma_free_cq(struct nvme_rdma_queue *queue)
424: {
425: if (nvme_rdma_poll_queue(queue))
426: ib_free_cq(queue->ib_cq);
427: else
428: ib_cq_pool_put(queue->ib_cq, queue->cq_size);
429: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 78/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 57880
output: 46
thoughts: 34
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as someone familiar with the underlying systems:
**Decoding this RDMA Call**
Alright, let's break this down. My initial reaction is that this call, specifically `ib_cq_pool_put`, is significant. This strongly suggests we're dealing with something related to RDMA, specifically, how completions are handled within an InfiniBand environment. The fact that it's `ib_cq_pool_put` is the key.
My immediate conclusion leans towards "nvme-rdma" being the relevant context. The use of InfiniBand Completion Queues (CQs) ties nicely into NVMe over Fabrics using RDMA. The thought process is to consider whether this call aligns with the known `nvme-rdma` driver behavior. This is further strengthened by the use of `ib_cq_pool_put`.
But, as always, I need to consider all possibilities. Let me also think about 'nvmet-rdma'. This is important to investigate, and is the next step to confirm my hypothesis regarding the correct NVMe over Fabrics specification, as it might behave differently or be part of a different part of the system.
|
| 79/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_create_queue_ib]
Results: map[SourceCode:1274: static int nvmet_rdma_create_queue_ib(struct nvmet_rdma_queue *queue)
1275: {
1276: struct ib_qp_init_attr qp_attr = { };
1277: struct nvmet_rdma_device *ndev = queue->dev;
1278: int nr_cqe, ret, i, factor;
1279:
1280: /*
1281: * Reserve CQ slots for RECV + RDMA_READ/RDMA_WRITE + RDMA_SEND.
1282: */
1283: nr_cqe = queue->recv_queue_size + 2 * queue->send_queue_size;
1284:
1285: queue->cq = ib_cq_pool_get(ndev->device, nr_cqe + 1,
1286: queue->comp_vector, IB_POLL_WORKQUEUE);
1287: if (IS_ERR(queue->cq)) {
1288: ret = PTR_ERR(queue->cq);
1289: pr_err("failed to create CQ cqe= %d ret= %d\n",
1290: nr_cqe + 1, ret);
1291: goto out;
1292: }
1293:
1294: qp_attr.qp_context = queue;
1295: qp_attr.event_handler = nvmet_rdma_qp_event;
1296: qp_attr.send_cq = queue->cq;
1297: qp_attr.recv_cq = queue->cq;
1298: qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
1299: qp_attr.qp_type = IB_QPT_RC;
1300: /* +1 for drain */
1301: qp_attr.cap.max_send_wr = queue->send_queue_size + 1;
1302: factor = rdma_rw_mr_factor(ndev->device, queue->cm_id->port_num,
1303: 1 << NVMET_RDMA_MAX_MDTS);
1304: qp_attr.cap.max_rdma_ctxs = queue->send_queue_size * factor;
1305: qp_attr.cap.max_send_sge = max(ndev->device->attrs.max_sge_rd,
1306: ndev->device->attrs.max_send_sge);
1307:
1308: if (queue->nsrq) {
1309: qp_attr.srq = queue->nsrq->srq;
1310: } else {
1311: /* +1 for drain */
1312: qp_attr.cap.max_recv_wr = 1 + queue->recv_queue_size;
1313: qp_attr.cap.max_recv_sge = 1 + ndev->inline_page_count;
1314: }
1315:
1316: if (queue->port->pi_enable && queue->host_qid)
1317: qp_attr.create_flags |= IB_QP_CREATE_INTEGRITY_EN;
1318:
1319: ret = rdma_create_qp(queue->cm_id, ndev->pd, &qp_attr);
1320: if (ret) {
1321: pr_err("failed to create_qp ret= %d\n", ret);
1322: goto err_destroy_cq;
1323: }
1324: queue->qp = queue->cm_id->qp;
1325:
1326: atomic_set(&queue->sq_wr_avail, qp_attr.cap.max_send_wr);
1327:
1328: pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %d cm_id= %p\n",
1329: __func__, queue->cq->cqe, qp_attr.cap.max_send_sge,
1330: qp_attr.cap.max_send_wr, queue->cm_id);
1331:
1332: if (!queue->nsrq) {
1333: for (i = 0; i < queue->recv_queue_size; i++) {
1334: queue->cmds[i].queue = queue;
1335: ret = nvmet_rdma_post_recv(ndev, &queue->cmds[i]);
1336: if (ret)
1337: goto err_destroy_qp;
1338: }
1339: }
1340:
1341: out:
1342: return ret;
1343:
1344: err_destroy_qp:
1345: rdma_destroy_qp(queue->cm_id);
1346: err_destroy_cq:
1347: ib_cq_pool_put(queue->cq, nr_cqe + 1);
1348: goto out;
1349: }
SourceFile:drivers/nvme/target/rdma.c]
|
| 80/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 59633
output: 43
thoughts: 24
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Dealing with an Error Path in the InfiniBand Code**
Hmm, so the error path involves a call to `ib_cq_pool_put`. That's standard practice, to ensure the completion queue resources get released properly when something goes wrong. But that brings up another potential issue I need to address. The real question is, what about `rtrs`? Are we handling the `rtrs` (likely referring to Receive Task Request Status) correctly in this scenario? Are we accounting for all possible states and their implications when an error is encountered during this flow? I need to double-check that the `rtrs` management is robust, particularly in this error handling routine. I want to make sure we're not leaking resources or leaving things in an inconsistent state.
|
| 81/2 |
2026/08/25 01:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/rtrs/rtrs.c Name:create_cq]
Results: map[SourceCode: 232: static int create_cq(struct rtrs_con *con, int cq_vector, int nr_cqe,
233: enum ib_poll_context poll_ctx)
234: {
235: struct rdma_cm_id *cm_id = con->cm_id;
236: struct ib_cq *cq;
237:
238: if (is_pollqueue(con))
239: cq = ib_alloc_cq(cm_id->device, con, nr_cqe, cq_vector,
240: poll_ctx);
241: else
242: cq = ib_cq_pool_get(cm_id->device, nr_cqe, cq_vector, poll_ctx);
243:
244: if (IS_ERR(cq)) {
245: rtrs_err(con->path, "Creating completion queue failed, errno: %pe\n",
246: cq);
247: return PTR_ERR(cq);
248: }
249: con->cq = cq;
250: con->nr_cqe = nr_cqe;
251:
252: return 0;
253: }
SourceFile:drivers/infiniband/ulp/rtrs/rtrs.c]
|
| 82/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 60270
output: 56
thoughts: 9
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Tracing the `create_cq` Function Call**
Okay, so the question immediately that pops up is: Where the hell is `create_cq` actually *used*? I need to understand the call graph to see what's relying on this thing. Is it central, a niche utility, or maybe even unused? I need to figure out where the dependency chain starts and ends. Am I looking at a core component or something buried deep within a particular module? Knowing the context of its use is going to be vital to understanding its function. Time to start digging through the codebase. I am going to figure out all the callers.
|
| 83/2 |
2026/08/25 01:34 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/infiniband/ulp/rtrs/rtrs.c IncludeSnippetLines:0 Name:create_cq SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:rtrs_cq_qp_create SourceFile:drivers/infiniband/ulp/rtrs/rtrs.c SourceLine:303]] TruncatedOutput:false]
|
| 84/2 |
2026/08/25 01:34 |
llm |
expert |
0m
Model:
Tokens:
input: 60565
output: 48
thoughts: 14
Thoughts: Okay, let's dive into this. Here's how I'd approach summarizing the prompt, given my expertise:
**Checking `rtrs_cq_qp_create`**
Right, so the prompt is extremely concise β it's just telling me to "check `rtrs_cq_qp_create`". That's it. Given my deep understanding of systems and typically the context in which I deal with such a command, I'm immediately thinking a few things. First, this likely refers to some kind of function, routine, or module within a system I'm familiar with. "cq" and "qp" are often (but not exclusively) associated with queuing and possibly quality of service, and "rtrs" might provide more clues from the context of my experience. So, I need to figure out *what* `rtrs_cq_qp_create` is.
This could mean a few things depending on how I interpret it and what kind of systems I work with. I'm going to need to figure out what library, system, or software framework this likely lives within to know the purpose of it. Without more context, this could be:
* **A function to create a queue pair (QP)**, within a resource, which is a common pattern in the types of low-level systems I work with.
* **A routine that handles the allocation or initialization** of data structures or resources related to a controlled queue within the system, potentially with quality of service implications.
* **A higher-level abstraction** of something that deals with queuing/queues.
My next steps will depend on the system. I will need to look up documentation, code, or other information. I will look to see how the naming convention fits within the system I am likely dealing with. I will need to see what information can be gleaned from the variables/parameters if I know about them. I may also need to check the wider system for references. This could be to confirm a specific behavior, troubleshoot a problem, or learn more about how a component works, all the while thinking about the design of this subsystem. I'm already mentally creating a map of where this fits into the whole. I am now beginning to think of where I can find the information in order to understand it. I am ready to begin.
|
| 85/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/rtrs/rtrs.c Name:rtrs_cq_qp_create]
Results: map[SourceCode: 296: int rtrs_cq_qp_create(struct rtrs_path *path, struct rtrs_con *con,
297: u32 max_send_sge, int cq_vector, int nr_cqe,
298: u32 max_send_wr, u32 max_recv_wr,
299: enum ib_poll_context poll_ctx)
300: {
301: int err;
302:
303: err = create_cq(con, cq_vector, nr_cqe, poll_ctx);
304: if (err)
305: return err;
306:
307: err = create_qp(con, path->dev->ib_pd, max_send_wr, max_recv_wr,
308: max_send_sge);
309: if (err) {
310: destroy_cq(con);
311: return err;
312: }
313: con->path = path;
314:
315: return 0;
316: }
SourceFile:drivers/infiniband/ulp/rtrs/rtrs.c]
|
| 86/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 61470
output: 43
thoughts: 9
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in my domain of expertise:
**Checking `destroy_cq`**
Alright, time to dive in. "Let's check `destroy_cq`." That simple phrase is the key. I need to understand what's happening with that function, method, or whatever it is in this context. It's likely a critical piece of the puzzle, probably involved in removing, deleting, or otherwise deconstructing a computational construct, a data structure, or some element within the system.
This could mean a multitude of things. Is it a cleanup function after a job is done? Perhaps it's part of a garbage collection mechanism for resources? Could it be a safety check before a major operation? I need to know what `cq` refers to, specifically. Is it a container queue? A cache? A computational query? The context will be everything.
My immediate thoughts are on potential problems: What are the preconditions for calling `destroy_cq`? Does it handle concurrency properly? Are there any race conditions that might lead to data corruption or leaks if it's called at the wrong time or in the wrong sequence? I need to analyze its behavior under different scenarios, especially edge cases like partial failures or concurrent access. Debugging is definitely on my mind. I hope there's some good logging around this thing! I also want to check for any dependencies on other functions or modules. Does it properly release any allocated memory or resources? Are there any side effects I need to be aware of? Best make sure I can track those side effects. Let's dig in.
|
| 87/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/rtrs/rtrs.c Name:destroy_cq]
Results: map[SourceCode: 285: static void destroy_cq(struct rtrs_con *con)
286: {
287: if (con->cq) {
288: if (is_pollqueue(con))
289: ib_free_cq(con->cq);
290: else
291: ib_cq_pool_put(con->cq, con->nr_cqe);
292: }
293: con->cq = NULL;
294: }
SourceFile:drivers/infiniband/ulp/rtrs/rtrs.c]
|
| 88/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 62041
output: 49
thoughts: 18
Thoughts: Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm discussing this with a fellow expert:
**Concerning `ib_cq_pool_put` and `iser`**
Right, so I'm processing something, and the code's hitting `ib_cq_pool_put`. Immediately my mind jumps to a practical concern. We're obviously managing completion queues here. But then, as always, I have to think about the bigger picture. Is this the right context? My next question is, what about `iser`? Are we properly taking `iser` into account? I am concerned the underlying RDMA operation is also dependent on `iser`. I have to connect the dots and make sure this function, this put operation, handles things related to `iser` seamlessly. We can't afford any disconnect in how the iSER layer's interacting with our completion queue management. Need to verify that the put operation is synchronizing appropriately for the iSER side.
|
| 89/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/iser/iser_verbs.c Name:iser_create_ib_conn_res]
Results: map[SourceCode: 223: /*
224: * iser_create_ib_conn_res - Queue-Pair (QP)
225: *
226: * Return: 0 on success, -1 on failure
227: */
228: static int iser_create_ib_conn_res(struct ib_conn *ib_conn)
229: {
230: struct iser_conn *iser_conn = to_iser_conn(ib_conn);
231: struct iser_device *device;
232: struct ib_device *ib_dev;
233: struct ib_qp_init_attr init_attr;
234: int ret = -ENOMEM;
235: unsigned int max_send_wr, cq_size;
236:
237: BUG_ON(ib_conn->device == NULL);
238:
239: device = ib_conn->device;
240: ib_dev = device->ib_device;
241:
242: /* +1 for drain */
243: if (ib_conn->pi_support)
244: max_send_wr = ISER_QP_SIG_MAX_REQ_DTOS + 1;
245: else
246: max_send_wr = ISER_QP_MAX_REQ_DTOS + 1;
247: max_send_wr = min(max_send_wr, ib_dev->attrs.max_qp_wr);
248:
249: cq_size = max_send_wr + ISER_QP_MAX_RECV_DTOS;
250: ib_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_SOFTIRQ);
251: if (IS_ERR(ib_conn->cq)) {
252: ret = PTR_ERR(ib_conn->cq);
253: goto cq_err;
254: }
255: ib_conn->cq_size = cq_size;
256:
257: memset(&init_attr, 0, sizeof(init_attr));
258:
259: init_attr.event_handler = iser_qp_event_callback;
260: init_attr.qp_context = (void *)ib_conn;
261: init_attr.send_cq = ib_conn->cq;
262: init_attr.recv_cq = ib_conn->cq;
263: /* +1 for drain */
264: init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS + 1;
265: init_attr.cap.max_send_sge = 2;
266: init_attr.cap.max_recv_sge = 1;
267: init_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
268: init_attr.qp_type = IB_QPT_RC;
269: init_attr.cap.max_send_wr = max_send_wr;
270: if (ib_conn->pi_support)
271: init_attr.create_flags |= IB_QP_CREATE_INTEGRITY_EN;
272: iser_conn->max_cmds = ISER_GET_MAX_XMIT_CMDS(max_send_wr - 1);
273:
274: ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr);
275: if (ret)
276: goto out_err;
277:
278: ib_conn->qp = ib_conn->cma_id->qp;
279: iser_info("setting conn %p cma_id %p qp %p max_send_wr %d\n", ib_conn,
280: ib_conn->cma_id, ib_conn->cma_id->qp, max_send_wr);
281: return ret;
282:
283: out_err:
284: ib_cq_pool_put(ib_conn->cq, ib_conn->cq_size);
285: cq_err:
286: iser_err("unable to alloc mem or create resource, err %d\n", ret);
287:
288: return ret;
289: }
SourceFile:drivers/infiniband/ulp/iser/iser_verbs.c]
|
| 90/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 63590
output: 49
thoughts: 30
Thoughts: Okay, here's my interpretation of those thoughts, framed as a first-person summary and tailored for an expert audience:
**Analyzing the Error Path in iSER Connection Handling**
Alright, I'm digging into this error path and seeing something that's making me pause. It looks like we're calling `ib_cq_pool_put` in this error handling routine. Okay, standard practice, freeing resources associated with the completion queue. But then my mind jumps to a related function, `iser_free_ib_conn_res`. Is that also happening in this error scenario, or only in specific connection teardown conditions? I need to clarify the relationship between these two functions in this particular error flow. We need to make sure we're not leaking resources here.
|
| 91/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/iser/iser_verbs.c Name:iser_free_ib_conn_res]
Results: map[SourceCode: 360: /**
361: * iser_free_ib_conn_res - release IB related resources
362: * @iser_conn: iser connection struct
363: * @destroy: indicator if we need to try to release the
364: * iser device and memory regoins pool (only iscsi
365: * shutdown and DEVICE_REMOVAL will use this).
366: *
367: * This routine is called with the iser state mutex held
368: * so the cm_id removal is out of here. It is Safe to
369: * be invoked multiple times.
370: */
371: static void iser_free_ib_conn_res(struct iser_conn *iser_conn, bool destroy)
372: {
373: struct ib_conn *ib_conn = &iser_conn->ib_conn;
374: struct iser_device *device = ib_conn->device;
375:
376: iser_info("freeing conn %p cma_id %p qp %p\n",
377: iser_conn, ib_conn->cma_id, ib_conn->qp);
378:
379: if (ib_conn->qp) {
380: rdma_destroy_qp(ib_conn->cma_id);
381: ib_cq_pool_put(ib_conn->cq, ib_conn->cq_size);
382: ib_conn->qp = NULL;
383: }
384:
385: if (destroy) {
386: if (iser_conn->rx_descs)
387: iser_free_rx_descriptors(iser_conn);
388:
389: if (device) {
390: iser_device_try_release(device);
391: ib_conn->device = NULL;
392: }
393: }
394: }
SourceFile:drivers/infiniband/ulp/iser/iser_verbs.c]
|
| 92/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 64430
output: 45
thoughts: 18
Thoughts: Here's your summary:
**Considering Infiniband Completion Queue Pool Operations**
Okay, so I'm looking at this code and it's making a call to `ib_cq_pool_put`. Immediately, my brain kicks in β it's all about resource management, specifically, returning a completion queue entry back to the pool. The `cq_pool` is critical for high-performance Infiniband operations, right? We're optimizing for avoiding costly allocations and deallocations. But... my thoughts are now drawn to `isert`. I need to start asking myself - is the isert code path also using the same pool, or does it have a separate one? What are the implications of the choice? If they're shared, we might need to look at contention and how that affects performance under load. This warrants a more in-depth investigation into how `isert` interacts with these core IB structures. I need to check the related code paths.
|
| 93/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_create_qp]
Results: map[SourceCode: SourceFile:]
|
| 94/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 64715
output: 45
thoughts: 29
Thoughts: Here's my attempt at summarizing the thought process, as if I were a knowledgeable user:
**Locating the ISER QP Creation Function**
Okay, so I'm trying to figure out where the `isert_create_qp` function resides within the Infiniband kernel drivers. Let's see... the name itself suggests it's related to iSER, or iSCSI Extensions for RDMA. So, first thought, it's likely within the relevant `drivers/infiniband` subsystem. Given "isert" specifically, I bet it is within the `ulp` which means "Upper Layer Protocol" directories. Ah, yes! And, looking more closely at the filesystem structure, it looks like it is located in `drivers/infiniband/ulp/isert/ib_isert.c`. Bingo!
|
| 95/2 |
2026/08/25 01:35 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_create_qp]
Results: map[SourceCode: SourceFile:]
|
| 96/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 64984
output: 43
thoughts: 44
Thoughts: Here's my thought process, summarized as you requested:
**Debugging Infiniband: A Quick Dive**
Okay, let's see. I need to track down where this `ib_cq_pool_get` function is defined. Hmm, let's fire up a `grep` and see what comes back. Alright, I've got it - I was expecting this to be in the Infiniband code, and it is! Looks like it's located in `drivers/infiniband/ulp/isert/ib_isert.c`, specifically on line 102. That's a good starting point. Now, I can start to analyze the context of that function call, understanding how it relates to iSER, and hopefully pinpoint the issue.
|
| 97/2 |
2026/08/25 01:35 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/ulp/isert/ib_isert.c FirstLine:90 LineCount:50]
Results: map[Contents: 90: ib_event_msg(e->event), e->event, isert_conn);
91:
92: switch (e->event) {
93: case IB_EVENT_COMM_EST:
94: rdma_notify(isert_conn->cm_id, IB_EVENT_COMM_EST);
95: break;
96: default:
97: break;
98: }
99: }
100:
101: static struct ib_qp *
102: isert_create_qp(struct isert_conn *isert_conn,
103: struct rdma_cm_id *cma_id)
104: {
105: u32 cq_size = ISERT_QP_MAX_REQ_DTOS + ISERT_QP_MAX_RECV_DTOS + 2;
106: struct isert_device *device = isert_conn->device;
107: struct ib_device *ib_dev = device->ib_device;
108: struct ib_qp_init_attr attr;
109: int ret, factor;
110:
111: isert_conn->cq = ib_cq_pool_get(ib_dev, cq_size, -1, IB_POLL_WORKQUEUE);
112: if (IS_ERR(isert_conn->cq)) {
113: isert_err("Unable to allocate cq\n");
114: ret = PTR_ERR(isert_conn->cq);
115: return ERR_PTR(ret);
116: }
117: isert_conn->cq_size = cq_size;
118:
119: memset(&attr, 0, sizeof(struct ib_qp_init_attr));
120: attr.event_handler = isert_qp_event_callback;
121: attr.qp_context = isert_conn;
122: attr.send_cq = isert_conn->cq;
123: attr.recv_cq = isert_conn->cq;
124: attr.cap.max_send_wr = ISERT_QP_MAX_REQ_DTOS + 1;
125: attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS + 1;
126: factor = rdma_rw_mr_factor(device->ib_device, cma_id->port_num,
127: isert_sg_tablesize);
128: attr.cap.max_rdma_ctxs = ISCSI_DEF_XMIT_CMDS_MAX * factor;
129: attr.cap.max_send_sge = device->ib_device->attrs.max_send_sge;
130: attr.cap.max_recv_sge = 1;
131: attr.sq_sig_type = IB_SIGNAL_REQ_WR;
132: attr.qp_type = IB_QPT_RC;
133: if (device->pi_capable)
134: attr.create_flags |= IB_QP_CREATE_INTEGRITY_EN;
135:
136: ret = rdma_create_qp(cma_id, device->pd, &attr);
137: if (ret) {
138: isert_err("rdma_create_qp failed for cma_id %d\n", ret);
139: ib_cq_pool_put(isert_conn->cq, isert_conn->cq_size);
]
|
| 98/2 |
2026/08/25 01:35 |
llm |
expert |
0m
Model:
Tokens:
input: 66206
output: 45
thoughts: 26
Thoughts: Okay, here's my take on those thoughts, framed as my own:
**Error Handling and Connection Cleanup - A Quick Scan**
Alright, so I see `ib_cq_pool_put` being called within the error path. That's good, expected behavior for managing completion queue entries. But, my immediate thought is to perform a quick checklist within my mental model and I ask myself, have I ensured `isert_free_conn` is properly handled in relation to this? This is crucial to prevent resource leaks and maintain data integrity in the context of InfiniBand. I need to be sure that the error handling encompasses the appropriate connection clean-up procedures. I need to review the surrounding code to confirm it's not being missed during an error.
|
| 99/2 |
2026/08/25 01:36 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_free_conn]
Results: map[SourceCode: SourceFile:]
|
| 100/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 66456
output: 43
thoughts: 33
Thoughts: Okay, here's my interpretation of that thought process, as if it were my own:
**Investigating a Potential Infiniband Issue in the iSER Driver**
Right, so I'm thinking I need to track down something related to `ib_cq_pool_put`. Given my knowledge of the Infiniband stack and likely problem areas, my gut tells me it's probably related to completion queue handling, maybe in the iSER driver. Let's start by zeroing in on that function. To do that, I'll go directly to the source code, specifically the `drivers/infiniband/ulp/isert/ib_isert.c` file. Searching in there is the most direct way to understand how the driver is interacting with that function. That'll be my starting point to troubleshoot any potential issue with completion queue management within the iSER context.
|
| 101/2 |
2026/08/25 01:36 |
tool |
grepper |
0m
Args: map[Expression:ib_cq_pool_put PathPrefix:drivers/infiniband/ulp/isert/ib_isert.c]
Results: map[Output:drivers/infiniband/ulp/isert/ib_isert.c=102=isert_create_qp(struct isert_conn *isert_conn,
--
drivers/infiniband/ulp/isert/ib_isert.c-138- isert_err("rdma_create_qp failed for cma_id %d\n", ret);
drivers/infiniband/ulp/isert/ib_isert.c:139: ib_cq_pool_put(isert_conn->cq, isert_conn->cq_size);
drivers/infiniband/ulp/isert/ib_isert.c-140-
--
drivers/infiniband/ulp/isert/ib_isert.c=405=isert_destroy_qp(struct isert_conn *isert_conn)
--
drivers/infiniband/ulp/isert/ib_isert.c-407- ib_destroy_qp(isert_conn->qp);
drivers/infiniband/ulp/isert/ib_isert.c:408: ib_cq_pool_put(isert_conn->cq, isert_conn->cq_size);
drivers/infiniband/ulp/isert/ib_isert.c-409-}
]
|
| 102/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 67028
output: 43
thoughts: 108
Thoughts: **Investigating a Potential CQ Pool Issue in iSER**
Okay, so I'm digging into this iSER code, specifically around the QP destruction. I see that `isert_destroy_qp` calls `ib_cq_pool_put`, which is exactly what it should do. That seems right. Wait a minute, if every ULP is properly putting the CQ back into the pool, how in the world is `cqe_used` ever non-zero? That just doesn't compute. Something's clearly off here. Is there a race condition? Maybe the counter isn't atomic?
Alright, time to back up and double-check those CQ pool functions. I need to take a closer look at `ib_cq_pool_put`. There has to be a bug somewhere, either in the "get" or the "put" path, and I'll bet it is here. I have a hunch... let's see what the code reveals.
|
| 103/2 |
2026/08/25 01:36 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:ib_cq_pool_put]
Results: map[SourceCode: 503: /**
504: * ib_cq_pool_put - Return a CQ taken from a shared pool.
505: * @cq: The CQ to return.
506: * @nr_cqe: The max number of cqes that the user had requested.
507: */
508: void ib_cq_pool_put(struct ib_cq *cq, unsigned int nr_cqe)
509: {
510: if (WARN_ON_ONCE(nr_cqe > cq->cqe_used))
511: return;
512:
513: spin_lock_irq(&cq->device->cq_pools_lock);
514: cq->cqe_used -= nr_cqe;
515: spin_unlock_irq(&cq->device->cq_pools_lock);
516: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 104/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 67658
output: 38
thoughts: 460
Thoughts: **Analyzing CQ Pool Usage in NVMe-RDMA**
Okay, hold on a second. I was initially concerned about how `ib_cq_pool_put` works, specifically whether it takes the device (`dev`) as an argument. I had to remind myself that it uses `cq->device` internally. That part's fine.
But then I started thinking about the lock on `dev->cq_pools_lock`. I see `ib_cq_pool_get` increments `cqe_used` under the lock. So far, so good, it's thread-safe.
Now, a red flag popped up: what if `ib_cq_pool_put` is called with a different `nr_cqe` than what was originally requested by `ib_cq_pool_get`? That would be a leak! I *must* investigate.
Let me double-check `nvme_rdma_create_cq`. Ah, yes:
```c
queue->ib_cq = ib_cq_pool_get(ibdev, queue->cq_size, comp_vector, IB_POLL_SOFTIRQ);
```
And the corresponding `nvme_rdma_free_cq`:
```c
ib_cq_pool_put(queue->ib_cq, queue->cq_size);
```
So the key here is `queue->cq_size`. If that value changes between the `get` and the `put`, we have a problem. Does `queue->cq_size` change? Let's trace it back. Right, it's set in `nvme_rdma_create_queue_ib`:
```c
queue->cq_size = cq_factor * queue->queue_size + 1;
```
It doesn't seem to get modified later. So, that's not the issue. Phew.
But wait! What about the *target* side? I need to check `nvmet_rdma_create_queue_ib` (or whatever the similar function is named).
```c
nr_cqe = queue->recv_queue_size + 2 * queue->send_queue_size;
queue->cq = ib_cq_pool_get(ndev->device, nr_cqe + 1, queue->comp_vector, IB_POLL_WORKQUEUE);
```
I'm also going to need to look at the corresponding free function there as well. I'll get back to that later.
|
| 105/2 |
2026/08/25 01:36 |
tool |
grepper |
0m
Args: map[Expression:ib_cq_pool_put PathPrefix:drivers/nvme/target/rdma.c]
Results: map[Output:drivers/nvme/target/rdma.c=1274=static int nvmet_rdma_create_queue_ib(struct nvmet_rdma_queue *queue)
--
drivers/nvme/target/rdma.c-1346-err_destroy_cq:
drivers/nvme/target/rdma.c:1347: ib_cq_pool_put(queue->cq, nr_cqe + 1);
drivers/nvme/target/rdma.c-1348- goto out;
--
drivers/nvme/target/rdma.c=1361=static void nvmet_rdma_destroy_queue_ib(struct nvmet_rdma_queue *queue)
--
drivers/nvme/target/rdma.c-1374- ib_destroy_qp(queue->qp);
drivers/nvme/target/rdma.c:1375: ib_cq_pool_put(queue->cq, queue->recv_queue_size + 2 *
drivers/nvme/target/rdma.c-1376- queue->send_queue_size + 1);
]
|
| 106/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 68988
output: 45
thoughts: 419
Thoughts: **Analyzing CQ Size Calculation and Management**
Okay, let's break this down systematically. The code's calculating completion queue (CQ) sizes, and I need to make sure everything is thread-safe and correct. The formula `queue->recv_queue_size + 2 * queue->send_queue_size + 1` is used, and it's good that these sizes are fixed at queue allocation. That simplifies things, eliminating a potential source of race conditions and making the calculation predictable.
Now, let's look at the `rtrs` part. The code retrieves a CQ using `ib_cq_pool_get`, storing it in `cq`, and saving the number of completion queue entries as `con->nr_cqe`. The corresponding cleanup uses `ib_cq_pool_put` and `con->nr_cqe`. Seems safe, so far. No problems with concurrent access here as far as I can see.
Next, I'll go over the `iser` CQ size management. We're setting `cq_size` with `max_send_wr + ISER_QP_MAX_RECV_DTOS`. Then, `ib_conn->cq` is allocated using `ib_cq_pool_get` with `cq_size`, and the size is stored in `ib_conn->cq_size`. The freeing is handled by `ib_cq_pool_put` using `ib_conn->cq_size`. This setup also looks sound. The allocation and deallocation are paired correctly and using the same size.
Moving on to `isert`, the pattern continues. `cq_size` is calculated as `ISERT_QP_MAX_REQ_DTOS + ISERT_QP_MAX_RECV_DTOS + 2`. The CQ is allocated using `ib_cq_pool_get` with `cq_size` and saved, and then, `isert_conn->cq_size` stores the size. The destruction follows the same `ib_cq_pool_put` and `isert_conn->cq_size` procedure. This seems like a reliable approach.
So, overall, the pattern appears consistent and safe. The CQ sizes are determined at allocation and are not modified during runtime, mitigating the potential race conditions. Allocation and deallocation are correctly paired, and appropriate sizes are stored for the releasing the CQs via the `ib_cq_pool_put` API. Now, I'll analyze the `srpt` code next.
|
| 107/2 |
2026/08/25 01:36 |
tool |
grepper |
0m
Args: map[Expression:ib_cq_pool_put PathPrefix:drivers/infiniband/ulp/srpt/ib_srpt.c]
Results: map[Output:drivers/infiniband/ulp/srpt/ib_srpt.c=1854=static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-1922- sq_size, ret);
drivers/infiniband/ulp/srpt/ib_srpt.c:1923: ib_cq_pool_put(ch->cq, ch->cq_size);
drivers/infiniband/ulp/srpt/ib_srpt.c-1924- sq_size = max(sq_size / 2, MIN_SRPT_SQ_SIZE);
--
drivers/infiniband/ulp/srpt/ib_srpt.c-1948- ch->qp = NULL;
drivers/infiniband/ulp/srpt/ib_srpt.c:1949: ib_cq_pool_put(ch->cq, ch->cq_size);
drivers/infiniband/ulp/srpt/ib_srpt.c-1950- goto out;
--
drivers/infiniband/ulp/srpt/ib_srpt.c=1953=static void srpt_destroy_ch_ib(struct srpt_rdma_ch *ch)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-1955- ib_destroy_qp(ch->qp);
drivers/infiniband/ulp/srpt/ib_srpt.c:1956: ib_cq_pool_put(ch->cq, ch->cq_size);
drivers/infiniband/ulp/srpt/ib_srpt.c-1957-}
]
|
| 108/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 70431
output: 50
thoughts: 75
Thoughts: Here's my summary, as if I were thinking these things:
**Analyzing the `srpt` Retry Mechanism**
Okay, hold on a second... looking at this code, it's clear that the `srpt` code has a retry loop built in. I'm seeing it right here: the `ib_cq_pool_put` and the `sq_size` adjustment. Interesting. It's putting the completion queue back into a pool, and then halving the `sq_size` if something goes wrong. That's a classic back-off strategy. I wonder what the criteria are for triggering this and how frequently it happens. Is this happening due to congestion? Memory exhaustion? Or something else? Let's take a closer look at the `srpt_create_ch_ib` function, I think that will provide more context on the setup and initial conditions of this retry loop. I need to understand what's *causing* the loop to execute.
|
| 109/2 |
2026/08/25 01:36 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/srpt/ib_srpt.c Name:srpt_create_ch_ib]
Results: map[SourceCode: SourceFile:]
|
| 110/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 70783
output: 48
thoughts: 21
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Line Number Check**
Okay, let me just double-check something... I'm looking at `srpt_create_ch_ib` and I could swear I've worked on this particular section before. Ah yes, there it is! Confirmed - it's at line 1854. Time to dive back in and refresh my memory about the surrounding code. No need to get bogged down, but I should definitely give it a quick scan before proceeding.
|
| 111/2 |
2026/08/25 01:36 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/ulp/srpt/ib_srpt.c FirstLine:1850 LineCount:100]
Results: map[Contents:1850: /**
1851: * srpt_create_ch_ib - create receive and send completion queues
1852: * @ch: SRPT RDMA channel.
1853: */
1854: static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
1855: {
1856: struct ib_qp_init_attr *qp_init;
1857: struct srpt_port *sport = ch->sport;
1858: struct srpt_device *sdev = sport->sdev;
1859: const struct ib_device_attr *attrs = &sdev->device->attrs;
1860: u32 sq_size = sport->port_attrib.srp_sq_size;
1861: int i, ret;
1862:
1863: WARN_ON(ch->rq_size < 1);
1864:
1865: ret = -ENOMEM;
1866: qp_init = kzalloc_obj(*qp_init);
1867: if (!qp_init)
1868: goto out;
1869:
1870: retry:
1871: ch->cq = ib_cq_pool_get(sdev->device, ch->rq_size + sq_size, -1,
1872: IB_POLL_WORKQUEUE);
1873: if (IS_ERR(ch->cq)) {
1874: ret = PTR_ERR(ch->cq);
1875: pr_err("failed to create CQ cqe= %d ret= %pe\n",
1876: ch->rq_size + sq_size, ch->cq);
1877: goto out;
1878: }
1879: ch->cq_size = ch->rq_size + sq_size;
1880:
1881: qp_init->qp_context = (void *)ch;
1882: qp_init->event_handler = srpt_qp_event;
1883: qp_init->send_cq = ch->cq;
1884: qp_init->recv_cq = ch->cq;
1885: qp_init->sq_sig_type = IB_SIGNAL_REQ_WR;
1886: qp_init->qp_type = IB_QPT_RC;
1887: /*
1888: * We divide up our send queue size into half SEND WRs to send the
1889: * completions, and half R/W contexts to actually do the RDMA
1890: * READ/WRITE transfers. Note that we need to allocate CQ slots for
1891: * both both, as RDMA contexts will also post completions for the
1892: * RDMA READ case.
1893: */
1894: qp_init->cap.max_send_wr = min(sq_size / 2, attrs->max_qp_wr);
1895: qp_init->cap.max_rdma_ctxs = sq_size / 2;
1896: qp_init->cap.max_send_sge = attrs->max_send_sge;
1897: qp_init->cap.max_recv_sge = 1;
1898: qp_init->port_num = ch->sport->port;
1899: if (sdev->use_srq)
1900: qp_init->srq = sdev->srq;
1901: else
1902: qp_init->cap.max_recv_wr = ch->rq_size;
1903:
1904: if (ch->using_rdma_cm) {
1905: ret = rdma_create_qp(ch->rdma_cm.cm_id, sdev->pd, qp_init);
1906: ch->qp = ch->rdma_cm.cm_id->qp;
1907: } else {
1908: ch->qp = ib_create_qp(sdev->pd, qp_init);
1909: if (!IS_ERR(ch->qp)) {
1910: ret = srpt_init_ch_qp(ch, ch->qp);
1911: if (ret)
1912: ib_destroy_qp(ch->qp);
1913: } else {
1914: ret = PTR_ERR(ch->qp);
1915: }
1916: }
1917: if (ret) {
1918: bool retry = sq_size > MIN_SRPT_SQ_SIZE;
1919:
1920: if (retry) {
1921: pr_debug("failed to create queue pair with sq_size = %u (%d) - retrying\n",
1922: sq_size, ret);
1923: ib_cq_pool_put(ch->cq, ch->cq_size);
1924: sq_size = max(sq_size / 2, MIN_SRPT_SQ_SIZE);
1925: goto retry;
1926: } else {
1927: pr_err("failed to create queue pair with sq_size = %u (%d)\n",
1928: sq_size, ret);
1929: goto err_destroy_cq;
1930: }
1931: }
1932:
1933: atomic_set(&ch->sq_wr_avail, qp_init->cap.max_send_wr);
1934:
1935: pr_debug("%s: max_cqe= %d max_sge= %d sq_size = %u ch= %p\n",
1936: __func__, ch->cq->cqe, qp_init->cap.max_send_sge,
1937: qp_init->cap.max_send_wr, ch);
1938:
1939: if (!sdev->use_srq)
1940: for (i = 0; i < ch->rq_size; i++)
1941: srpt_post_recv(sdev, ch, ch->ioctx_recv_ring[i]);
1942:
1943: out:
1944: kfree(qp_init);
1945: return ret;
1946:
1947: err_destroy_cq:
1948: ch->qp = NULL;
1949: ib_cq_pool_put(ch->cq, ch->cq_size);
]
|
| 112/2 |
2026/08/25 01:36 |
llm |
expert |
0m
Model:
Tokens:
input: 72740
output: 39
thoughts: 319
Thoughts: **Analyzing the SRP Target Bug**
Okay, so `srpt` is definitely an SRP target, and configfs is the key. But hold on, can unprivileged users even mess with configfs? Nope, it's not mountable in user namespaces. Right, I remember, syzbot *could* potentially trigger configfs if it's running in `init_net` with root.
Hmm, but the bug report mentioned `smbdirect`, and the connection to `ib_cq_pool_get` is what I am trying to understand. I checked the `smbdirect` code β it uses `ib_alloc_cq_any`. Okay, `ib_alloc_cq_any` calls `__ib_alloc_cq_any`, which calls `__ib_alloc_cq`. Does *that* call `ib_cq_pool_get`? No, doesn't look like it. Does `smbdirect` itself directly use `ib_cq_pool_get`? Still no.
Then why did that damned bug report say `smbdirect: ib_dev[syz1] removed`? I get it! `smbdirect` is just a client, one of many that get removed when the device is unregistered. The warning is in `ib_cq_pool_cleanup`.
Right, let me think. `ib_cq_pool_cleanup` iterates over `dev->cq_pools`. If `cq->cqe_used` is non-zero, it means someone leaked CQs. But then what if `ib_cq_pool_cleanup` is called *before* all the clients are removed? That's what I need to look at. Let's check `disable_device`.
|
| 113/2 |
2026/08/25 01:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/device.c Name:disable_device]
Results: map[SourceCode:1304: static void disable_device(struct ib_device *device)
1305: {
1306: u32 cid;
1307:
1308: WARN_ON(!refcount_read(&device->refcount));
1309:
1310: down_write(&devices_rwsem);
1311: xa_clear_mark(&devices, device->index, DEVICE_REGISTERED);
1312: up_write(&devices_rwsem);
1313:
1314: /*
1315: * Remove clients in LIFO order, see assign_client_id. This could be
1316: * more efficient if xarray learns to reverse iterate. Since no new
1317: * clients can be added to this ib_device past this point we only need
1318: * the maximum possible client_id value here.
1319: */
1320: down_read(&clients_rwsem);
1321: cid = highest_client_id;
1322: up_read(&clients_rwsem);
1323: while (cid) {
1324: cid--;
1325: remove_client_context(device, cid);
1326: }
1327:
1328: ib_cq_pool_cleanup(device);
1329:
1330: /* Pairs with refcount_set in enable_device */
1331: ib_device_put(device);
1332: wait_for_completion(&device->unreg_completion);
1333:
1334: /*
1335: * compat devices must be removed after device refcount drops to zero.
1336: * Otherwise init_net() may add more compatdevs after removing compat
1337: * devices and before device is disabled.
1338: */
1339: remove_compat_devs(device);
1340: }
SourceFile:drivers/infiniband/core/device.c]
|
| 114/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 74026
output: 34
thoughts: 243
Thoughts: **My Concerns Regarding Client Context Removal**
Okay, so `remove_client_context` triggers the client's `remove` callback. But hold on a second... what if the client doesn't actually free its completion queue (CQ) *within* that callback? That's a critical oversight. I'm thinking of examples like `nvme-rdma`. They might be designed to hold off on CQ cleanup, maybe waiting for a separate event to signal the coast is clear. `iser` could behave the same way, right?
Then, the syzbot tests... If syzbot is involved, it likely created and unregistered a device. They use `syz_ib_setup` or something similar for the IB devices like `rxe` or `siw`. It then creates resources based on that device. So, syzbot creates its IB devices by using `rdma_rxe` or `siw`!
Now, the important question is: Can unprivileged users also create these `rdma_rxe` or `siw` devices? If so, we have a serious potential vulnerability. And the answer is yes, they can, through netlink. `RDMA_NL_NLDEV` allows for the creation of `rxe` and `siw` devices.
Alright, so can this `RDMA_NL_NLDEV` access be leveraged within user namespaces? That's the key. I need to dig into `rdma_nl_rcv_msg` to see what the permission model is to ensure there is no leak.
|
| 115/2 |
2026/08/25 01:37 |
tool |
grepper |
0m
Args: map[Expression:RDMA_NL_NLDEV PathPrefix:drivers/infiniband/]
Results: map[Output:drivers/infiniband/core/netlink.c=61=static bool is_nl_msg_valid(unsigned int type, unsigned int op)
--
drivers/infiniband/core/netlink.c-65- [RDMA_NL_LS] = RDMA_NL_LS_NUM_OPS,
drivers/infiniband/core/netlink.c:66: [RDMA_NL_NLDEV] = RDMA_NLDEV_NUM_OPS,
drivers/infiniband/core/netlink.c-67- };
--
drivers/infiniband/core/netlink.c=82=get_cb_table(const struct sk_buff *skb, unsigned int type, unsigned int op)
--
drivers/infiniband/core/netlink.c-89- */
drivers/infiniband/core/netlink.c:90: if (sock_net(skb->sk) != &init_net && type != RDMA_NL_NLDEV)
drivers/infiniband/core/netlink.c-91- return NULL;
--
drivers/infiniband/core/nldev.c=1121=static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-1147- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1148: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
drivers/infiniband/core/nldev.c-1149- 0, 0);
--
drivers/infiniband/core/nldev.c=1234=static int _nldev_get_dumpit(struct ib_device *device,
--
drivers/infiniband/core/nldev.c-1245- nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1246: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
drivers/infiniband/core/nldev.c-1247- 0, NLM_F_MULTI);
--
drivers/infiniband/core/nldev.c=1271=static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-1305- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1306: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
drivers/infiniband/core/nldev.c-1307- 0, 0);
--
drivers/infiniband/core/nldev.c=1329=static int nldev_port_get_dumpit(struct sk_buff *skb,
--
drivers/infiniband/core/nldev.c-1368- cb->nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1369: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-1370- RDMA_NLDEV_CMD_PORT_GET),
--
drivers/infiniband/core/nldev.c=1387=static int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-1416- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1417: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET),
drivers/infiniband/core/nldev.c-1418- 0, 0);
--
drivers/infiniband/core/nldev.c=1439=static int _nldev_res_get_dumpit(struct ib_device *device,
--
drivers/infiniband/core/nldev.c-1450- nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1451: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET),
drivers/infiniband/core/nldev.c-1452- 0, NLM_F_MULTI);
--
drivers/infiniband/core/nldev.c=1534=res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-1585- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1586: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-1587- RDMA_NL_GET_OP(nlh->nlmsg_type)),
--
drivers/infiniband/core/nldev.c=1615=static int res_get_common_dumpit(struct sk_buff *skb,
--
drivers/infiniband/core/nldev.c-1668- nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1669: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-1670- RDMA_NL_GET_OP(cb->nlh->nlmsg_type)),
--
drivers/infiniband/core/nldev.c=1906=static int nldev_get_chardev(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-1949- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:1950: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-1951- RDMA_NLDEV_CMD_GET_CHARDEV),
--
drivers/infiniband/core/nldev.c=1994=static int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-2010- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2011: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2012- RDMA_NLDEV_CMD_SYS_GET),
--
drivers/infiniband/core/nldev.c=2207=static int nldev_stat_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-2244- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2245: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2246- RDMA_NLDEV_CMD_STAT_SET),
--
drivers/infiniband/core/nldev.c=2277=static int nldev_stat_del_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-2313- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2314: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2315- RDMA_NLDEV_CMD_STAT_SET),
--
drivers/infiniband/core/nldev.c=2348=stat_get_doit_default_counter(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-2386- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2387: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2388- RDMA_NLDEV_CMD_STAT_GET),
--
drivers/infiniband/core/nldev.c=2441=stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh,
--
drivers/infiniband/core/nldev.c-2477- nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2478: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2479- RDMA_NLDEV_CMD_STAT_GET),
--
drivers/infiniband/core/nldev.c=2577=static int nldev_stat_get_counter_status_doit(struct sk_buff *skb,
--
drivers/infiniband/core/nldev.c-2618- msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2619: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_STAT_GET_STATUS),
drivers/infiniband/core/nldev.c-2620- 0, 0);
--
drivers/infiniband/core/nldev.c=2835=static int nldev_frmr_pools_get_dumpit(struct sk_buff *skb,
--
drivers/infiniband/core/nldev.c-2870- nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
drivers/infiniband/core/nldev.c:2871: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
drivers/infiniband/core/nldev.c-2872- RDMA_NLDEV_CMD_FRMR_POOLS_GET),
--
drivers/infiniband/core/nldev.c=3199=int rdma_nl_notify_event(struct ib_device *device, u32 port_num,
--
drivers/infiniband/core/nldev.c-3214- nlh = nlmsg_put(skb, 0, 0,
drivers/infiniband/core/nldev.c:3215: RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_MONITOR),
drivers/infiniband/core/nldev.c-3216- 0, 0);
--
drivers/infiniband/core/nldev.c=3261=void __init nldev_init(void)
drivers/infiniband/core/nldev.c-3262-{
drivers/infiniband/core/nldev.c:3263: rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table);
drivers/infiniband/core/nldev.c-3264-}
--
drivers/infiniband/core/nldev.c=3266=void nldev_exit(void)
drivers/infiniband/core/nldev.c-3267-{
drivers/infiniband/core/nldev.c:3268: rdma_nl_unregister(RDMA_NL_NLDEV);
drivers/infiniband/core/nldev.c-3269-}
drivers/infiniband/core/nldev.c-3270-
drivers/infiniband/core/nldev.c:3271:MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_NLDEV, 5);
]
|
| 116/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 78219
output: 45
thoughts: 263
Thoughts: **RDMA Netlink Family Access: A Deep Dive**
Okay, so I'm processing this code snippet and the implications are significant. It seems that `RDMA_NL_NLDEV` is the *only* RDMA netlink family accessible from user namespaces. That's a crucial point, and it immediately makes me think about potential security ramifications. The code seems to be explicitly restricting access to other families.
So, this means a user-space process can interact with `RDMA_NL_NLDEV` and therefore, potentially, create `siw` or `rxe` devices. Aha! Now, if syzbot, for instance, triggers the creation of an `rxe` device, that device will get registered. That registration involves a call to `ib_register_device`. This function then calls `enable_device_and_get`, which leads to a call to `ib_alloc_cqs`. And after this, there's notification of clients.
Now, which clients are being notified? Think `smbdirect`, `nvme-rdma`, `iser`, and others. Here's where it gets interesting. I know `smbdirect` *doesn't* use `ib_cq_pool_get`. But `iser` does! Wait a minute, `iser` is a client, right? But doesn't it only create resources when a connection is established? So that means it probably doesn't call `ib_cq_pool_get` in the `add` callback.
I need to confirm this. I need to go look at `iser_add_one` and confirm this assumption. This is a critical point that needs verification.
|
| 117/2 |
2026/08/25 01:37 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/iser/iser_verbs.c Name:iser_add_one]
Results: map[SourceCode: SourceFile:]
|
| 118/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 78904
output: 36
thoughts: 16
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing `ib_register_client` Callbacks for `iser` Functionality**
Okay, I'm diving into the `ib_register_client` callbacks, specifically to understand their role in the `iser` (iSCSI Extensions for RDMA) implementation. My immediate focus is on how these callbacks are leveraged within the InfiniBand framework. I need to refresh my understanding of how the client registration process interacts with the low-level RDMA operations that are at the heart of `iser`. Specifically, I'm thinking about:
* **Initialization:** How does `iser` use these callbacks during initialization? Is it primarily for device discovery, resource allocation, or something else entirely?
* **Event Handling:** What events does `iser` register for, and how are these callbacks used to respond? This is crucial for understanding how connection establishment, data transfer, and error handling are managed at the IB layer. I'm keen to see if they're using specific event codes.
* **Resource Management:** Are these callbacks involved in managing memory regions (MRs), protection domains (PDs), and queue pairs (QPs) related to `iser`? Or are those handled through a different mechanism? I expect to see `ib_mr`, `ib_pd`, and `ib_qp` interactions in play.
* **Integration Points:** Where are the key integration points between the iSCSI layer and the InfiniBand layer, and how are these callbacks responsible for facilitating that communication? I'm anticipating that callbacks are used to translate iSCSI commands and data into RDMA operations.
I'm particularly interested in looking for any subtle or non-obvious interactions within these callbacks that could contribute to performance bottlenecks, resource leaks, or potential security vulnerabilities. Time to dig into the source code!
|
| 119/2 |
2026/08/25 01:37 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_register_client PathPrefix:drivers/infiniband/ulp/iser/]
Results:
|
| 120/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 79367
output: 34
thoughts: 30
Thoughts: Okay, here's my interpretation of those thoughts, presented as a personal thought process, as requested:
**Navigating the iSER Implementation**
Hmm, something doesn't feel right. My intuition is telling me the `iser` driver isn't utilizing `ib_register_client` in the way I'd expect. That seems unusual, given its role in handling InfiniBand registration. I need to confirm this. Time to dig into the `iscsi_iser_transport` source code and see exactly how they're handling registration and the client connection. This is where the core logic will be. I need to understand this to have the full picture. Let's see what's happening.
|
| 121/2 |
2026/08/25 01:37 |
tool |
grepper |
0m
Args: map[Expression:ib_register_client PathPrefix:drivers/infiniband/ulp/]
Results: map[Output:drivers/infiniband/ulp/ipoib/ipoib_main.c=2725=static int __init ipoib_init_module(void)
--
drivers/infiniband/ulp/ipoib/ipoib_main.c-2766-
drivers/infiniband/ulp/ipoib/ipoib_main.c:2767: ret = ib_register_client(&ipoib_client);
drivers/infiniband/ulp/ipoib/ipoib_main.c-2768- if (ret)
--
drivers/infiniband/ulp/rtrs/rtrs-srv.c=2254=struct rtrs_srv_ctx *rtrs_srv_open(struct rtrs_srv_ops *ops, u16 port)
--
drivers/infiniband/ulp/rtrs/rtrs-srv.c-2266-
drivers/infiniband/ulp/rtrs/rtrs-srv.c:2267: err = ib_register_client(&rtrs_srv_client);
drivers/infiniband/ulp/rtrs/rtrs-srv.c-2268- if (err) {
--
drivers/infiniband/ulp/srp/ib_srp.c=4177=static int __init srp_init_module(void)
--
drivers/infiniband/ulp/srp/ib_srp.c-4236-
drivers/infiniband/ulp/srp/ib_srp.c:4237: ret = ib_register_client(&srp_client);
drivers/infiniband/ulp/srp/ib_srp.c-4238- if (ret) {
--
drivers/infiniband/ulp/srpt/ib_srpt.c=3903=static const struct target_core_fabric_ops srpt_template = {
--
drivers/infiniband/ulp/srpt/ib_srpt.c-3942- *
drivers/infiniband/ulp/srpt/ib_srpt.c:3943: * Note: Since ib_register_client() registers callback functions, and since at
drivers/infiniband/ulp/srpt/ib_srpt.c-3944- * least one of these callback functions (srpt_add_one()) calls target core
drivers/infiniband/ulp/srpt/ib_srpt.c-3945- * functions, this driver must be registered with the target core before
drivers/infiniband/ulp/srpt/ib_srpt.c:3946: * ib_register_client() is called.
drivers/infiniband/ulp/srpt/ib_srpt.c-3947- */
drivers/infiniband/ulp/srpt/ib_srpt.c=3948=static int __init srpt_init_module(void)
--
drivers/infiniband/ulp/srpt/ib_srpt.c-3969-
drivers/infiniband/ulp/srpt/ib_srpt.c:3970: ret = ib_register_client(&srpt_client);
drivers/infiniband/ulp/srpt/ib_srpt.c-3971- if (ret) {
]
|
| 122/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 80410
output: 44
thoughts: 67
Thoughts: Here's my summary, as though I'm thinking through this myself:
**RDMA Client Registration: A Quick Divergence**
Okay, so I'm trying to wrap my head around this RDMA client registration thing. I've got this pattern forming... It looks like `iser` and `isert` are avoiding `ib_register_client`, which is interesting. They seem to be leaning towards using `rdma_cm` instead. I wonder why that design choice was made, perhaps performance or some other factor I should investigate later. On the other hand, `nvme-rdma` and `nvmet-rdma` are clearly using `ib_register_client`. Let's confirm that by checking out the `nvme_rdma_add_one` function. Time to dig in and see what's happening there!
|
| 123/2 |
2026/08/25 01:37 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_add_one]
Results: map[SourceCode: SourceFile:]
|
| 124/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 80721
output: 44
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Deep Dive into `nvme_rdma_ib_client`**
Alright, time to dig into this `nvme_rdma_ib_client` code. I need to refresh my understanding of how this specific client implementation handles the NVMe over RDMA/InfiniBand interaction. I'm thinking about the control path, the data path, and how it handles potential error conditions. Specifically, I want to see how the client abstracts the complexities of the IB verbs and presents a clean NVMe interface to the upper layers. I'm looking for the nuances in how it negotiates the RDMA connection, how it handles CQE processing, and any specific optimizations theyβve implemented to maximize throughput and minimize latency. Let's see how well it copes with different types of I/O workloads - is there anything here that might be a bottleneck, or where we could possibly push performance even further? Time to get to work.
|
| 125/2 |
2026/08/25 01:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_ib_client]
Results: map[SourceCode:2438: static struct ib_client nvme_rdma_ib_client = {
2439: .name = "nvme_rdma",
2440: .remove = nvme_rdma_remove_one
2441: };
SourceFile:drivers/nvme/host/rdma.c]
|
| 126/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 81101
output: 44
thoughts: 24
Thoughts: Okay, here's my interpretation of those thoughts, summarized in the first person, assuming an expert user:
**Hmm, Just "Remove"?**
Alright, so I'm looking at this... and all I'm seeing is a `remove` function. Interesting. Seems... limited. My first thought is: that's it? Where's the counterpart to handle, you know, everything *related* to `nvmet_rdma_ib_client`? I'd expect to see a more comprehensive set of routines, especially given the complexity of the RDMA IB client context. It seems strange that there isn't more. Maybe I'm missing something, but this seems very... bare-bones.
|
| 127/2 |
2026/08/25 01:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_ib_client]
Results: map[SourceCode:2126: static struct ib_client nvmet_rdma_ib_client = {
2127: .name = "nvmet_rdma",
2128: .remove = nvmet_rdma_remove_one
2129: };
SourceFile:drivers/nvme/target/rdma.c]
|
| 128/2 |
2026/08/25 01:37 |
llm |
expert |
0m
Model:
Tokens:
input: 81422
output: 42
thoughts: 765
Thoughts: **Debugging a Mysterious RDMA CQ Issue**
Okay, so I'm looking at this issue where `cqe_used` is non-zero, but the allocation method seems unusual. My initial thought is that the code only has a `remove` function but not an `add` function. If the code does nothing on `add`, it doesn't allocate completion queues (CQs) when the device is first added. My thinking is that the CQs are allocated during connection establishment. But syzbot is testing, and could be establishing a connection.
Syzbot is able to create a device using `RDMA_NL_NLDEV`. Now, can syzbot establish a connection with this device? Could it be using `rdma_ucm` (RDMA User Connection Manager)? `rdma_ucm` allows unprivileged users to create QPs and CQs, which would make sense for syzbot. However, `rdma_ucm` uses `ib_alloc_cq`, not `ib_cq_pool_get`! So that is not it.
Wait, `ib_uverbs` allows userspace to create CQs. It uses `ib_alloc_cq_any`? No, it uses `ib_create_cq`. So, the question remains: if no ULP is accessible and `rdma_ucm` doesn't use `ib_cq_pool_get`, how is `cqe_used` non-zero? That's the core of the mystery.
Okay, let's see if `ib_cq_pool_get` is used anywhere else. I performed a global grep and found that it's only used by ULPs. Could something else be modifying `cqe_used`? I checked the references to `cqe_used` and it's only modified by `ib_cq_pool_get` and `ib_cq_pool_put`.
Wait, what if `ib_alloc_cqs` fails and leaves `cqe_used` uninitialized? No, `ib_alloc_cqs` uses `ib_alloc_cq` which uses `kzalloc`. What about memory corruption? But syzbot reports a WARNING, not a crash.
Could `ib_cq_pool_get` be called by `rtrs` or `srpt`? `rtrs` uses sysfs, and syzbot can't write to sysfs. Wait, but syzbot *can* write to sysfs if it runs in `init_net` with `CAP_SYS_ADMIN`! Syzbot often runs tests in the initial namespace with root privileges. So, if syzbot has root, it can trigger `nvme-rdma`, `nvmet-rdma`, `rtrs`, `srpt`, `iser`, `isert`. The bug is likely in one of these ULPs or the core.
Let's look at the bug report again. It says `smbdirect: ib_dev[syz1] removed`. This tells me that `smbdirect` was loaded and its `remove` callback was called. If syzbot has root, it can also mount CIFS with `smbdirect`. Does `smbdirect` use `ib_cq_pool_get`? I checked `smbdirect`, and it uses `ib_alloc_cq_any`.
Wait, `ib_alloc_cq_any` calls `__ib_alloc_cq_any` which calls `__ib_alloc_cq`. Does `__ib_alloc_cq` use `ib_cq_pool_get`? No. But wait! `ib_alloc_cq_any` allocates a CQ. Does it add it to `dev->cq_pools`? Let's check `__ib_alloc_cq`.
|
| 129/2 |
2026/08/25 01:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/core/cq.c Name:__ib_alloc_cq]
Results: map[SourceCode: 198: /**
199: * __ib_alloc_cq - allocate a completion queue
200: * @dev: device to allocate the CQ for
201: * @private: driver private data, accessible from cq->cq_context
202: * @nr_cqe: number of CQEs to allocate
203: * @comp_vector: HCA completion vectors for this CQ
204: * @poll_ctx: context to poll the CQ from.
205: * @caller: module owner name.
206: *
207: * This is the proper interface to allocate a CQ for in-kernel users. A
208: * CQ allocated with this interface will automatically be polled from the
209: * specified context. The ULP must use wr->wr_cqe instead of wr->wr_id
210: * to use this CQ abstraction.
211: */
212: struct ib_cq *__ib_alloc_cq(struct ib_device *dev, void *private, int nr_cqe,
213: int comp_vector, enum ib_poll_context poll_ctx,
214: const char *caller)
215: {
216: struct ib_cq_init_attr cq_attr = {
217: .cqe = nr_cqe,
218: .comp_vector = comp_vector,
219: };
220: struct ib_cq *cq;
221: int ret = -ENOMEM;
222:
223: if (WARN_ON_ONCE(!nr_cqe))
224: return ERR_PTR(-EINVAL);
225:
226: cq = rdma_zalloc_drv_obj(dev, ib_cq);
227: if (!cq)
228: return ERR_PTR(ret);
229:
230: cq->device = dev;
231: cq->cq_context = private;
232: cq->poll_ctx = poll_ctx;
233: atomic_set(&cq->usecnt, 0);
234: cq->comp_vector = comp_vector;
235:
236: cq->wc = kmalloc_objs(*cq->wc, IB_POLL_BATCH);
237: if (!cq->wc)
238: goto out_free_cq;
239:
240: rdma_restrack_new(&cq->res, RDMA_RESTRACK_CQ);
241: rdma_restrack_set_name(&cq->res, caller);
242:
243: ret = dev->ops.create_cq(cq, &cq_attr, NULL);
244: if (ret)
245: goto out_free_wc;
246:
247: rdma_dim_init(cq);
248:
249: switch (cq->poll_ctx) {
250: case IB_POLL_DIRECT:
251: cq->comp_handler = ib_cq_completion_direct;
252: break;
253: case IB_POLL_SOFTIRQ:
254: cq->comp_handler = ib_cq_completion_softirq;
255:
256: irq_poll_init(&cq->iop, IB_POLL_BUDGET_IRQ, ib_poll_handler);
257: ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
258: break;
259: case IB_POLL_WORKQUEUE:
260: case IB_POLL_UNBOUND_WORKQUEUE:
261: cq->comp_handler = ib_cq_completion_workqueue;
262: INIT_WORK(&cq->work, ib_cq_poll_work);
263: ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
264: cq->comp_wq = (cq->poll_ctx == IB_POLL_WORKQUEUE) ?
265: ib_comp_wq : ib_comp_unbound_wq;
266: break;
267: default:
268: ret = -EINVAL;
269: goto out_destroy_cq;
270: }
271:
272: rdma_restrack_add(&cq->res);
273: trace_cq_alloc(cq, nr_cqe, comp_vector, poll_ctx);
274: return cq;
275:
276: out_destroy_cq:
277: rdma_dim_destroy(cq);
278: cq->device->ops.destroy_cq(cq, NULL);
279: out_free_wc:
280: rdma_restrack_put(&cq->res);
281: kfree(cq->wc);
282: out_free_cq:
283: kfree(cq);
284: trace_cq_alloc_error(nr_cqe, comp_vector, poll_ctx, ret);
285: return ERR_PTR(ret);
286: }
SourceFile:drivers/infiniband/core/cq.c]
|
| 130/2 |
2026/08/25 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 84531
output: 59
thoughts: 667
Thoughts: **Debugging a CQE Leak**
Okay, so I'm trying to track down this CQE leak. It's definitely not being added to `dev->cq_pools` directly; only `ib_alloc_cqs` adds to the pool. The code iterates, allocates a CQ, sets `cq->shared` to true, adds it to a temporary list, and then splices that list into `dev->cq_pools`. That means if a CQ is in `ib_cq_pool_cleanup`, it *must* be a shared CQ allocated this way, and `cqe_used` *must* have been incremented by `ib_cq_pool_get`.
So, it's a shared CQ and we're *supposed* to be using CQEs. The leak means something isn't decrementing `cqe_used` correctly. I need to figure out which ULP is failing to release the CQEs.
My first thought: what if `ib_cq_pool_get` is called but then the ULP fails to create the QP and calls `ib_cq_pool_put`? I've checked the error paths in the core code and they all seem to call `ib_cq_pool_put`. But I need to be sure.
Then, I started thinking about the `nvme-rdma` ULP. Let's dig into `nvme_rdma_create_queue_ib` specifically. It calls `nvme_rdma_create_cq`, then `nvme_rdma_create_qp`. And if `nvme_rdma_create_qp` fails, it jumps to `out_destroy_ib_cq` and calls `nvme_rdma_free_cq`. I looked at `nvme_rdma_free_cq` and it checks `nvme_rdma_poll_queue`. If it's a poll queue, it calls `ib_free_cq`. Otherwise, it calls `ib_cq_pool_put` with the `cq_size`. That looks correct!
Then I started to think, *what* if `nvme_rdma_alloc_queue` fails *after* `nvme_rdma_create_queue_ib` is called? Wait, is `nvme_rdma_create_queue_ib` even *called* from `nvme_rdma_alloc_queue`?
Okay, I just verified `nvme_rdma_alloc_queue` doesn't call `nvme_rdma_create_queue_ib`. I need to track down where `nvme_rdma_create_queue_ib` is actually called. I'm getting closer, but I know I'll have to consider all paths that call `ib_cq_pool_get` now and verify proper error handling, but I think the leak is in one of these paths that calls `ib_cq_pool_get`, but does not release all the CQs.
|
| 131/2 |
2026/08/25 01:38 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c IncludeSnippetLines:0 Name:nvme_rdma_create_queue_ib SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvme_rdma_addr_resolved SourceFile:drivers/nvme/host/rdma.c SourceLine:1853]] TruncatedOutput:false]
|
| 132/2 |
2026/08/25 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 85979
output: 44
thoughts: 32
Thoughts: Here's my take on the thought process, as requested:
**Diving into the RDMA Address Resolution**
Okay, so the trace indicates `nvme_rdma_addr_resolved` is the function we're looking at. Excellent! That's the key. Let's dig into `nvme_rdma_addr_resolved` and see what it's doing. This is where the RDMA address resolution magic happens, so let's get down to brass tacks and understand how this is being handled. I need to understand what's happening.
|
| 133/2 |
2026/08/25 01:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_addr_resolved]
Results: map[SourceCode:1848: static int nvme_rdma_addr_resolved(struct nvme_rdma_queue *queue)
1849: {
1850: struct nvme_ctrl *ctrl = &queue->ctrl->ctrl;
1851: int ret;
1852:
1853: ret = nvme_rdma_create_queue_ib(queue);
1854: if (ret)
1855: return ret;
1856:
1857: if (ctrl->opts->tos >= 0)
1858: rdma_set_service_type(queue->cm_id, ctrl->opts->tos);
1859: ret = rdma_resolve_route(queue->cm_id, NVME_RDMA_CM_TIMEOUT_MS);
1860: if (ret) {
1861: dev_err(ctrl->device, "rdma_resolve_route failed (%d).\n",
1862: queue->cm_error);
1863: goto out_destroy_queue;
1864: }
1865:
1866: return 0;
1867:
1868: out_destroy_queue:
1869: nvme_rdma_destroy_queue_ib(queue);
1870: return ret;
1871: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 134/2 |
2026/08/25 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 86571
output: 46
thoughts: 38
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a Queue Destruction Error**
Alright, so the system flagged an error, and it's calling `nvme_rdma_destroy_queue_ib(queue)`. That's the cleanup function for our RDMA-based NVMe queue, not unexpected in an error scenario. Now, the core question is *why* it got to that point. This function is critical; it ensures we're properly releasing all associated RDMA resources when a queue is no longer needed or encounters a problem. So, where does this get called in the larger function? What are the possible error paths that lead to this? Let me quickly review the surrounding code. Need to verify that queue teardown is handled correctly in all error cases. Let's dig into `nvme_rdma_destroy_queue_ib` itself and see if anything jumps out as immediately problematic; I'm looking for resource leaks, incorrect state management, or anything else that might have triggered this cleanup in the first place. This may be a symptom, not the root cause, but it's the first tangible piece of evidence to work with.
|
| 135/2 |
2026/08/25 01:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_destroy_queue_ib]
Results: map[SourceCode: 431: static void nvme_rdma_destroy_queue_ib(struct nvme_rdma_queue *queue)
432: {
433: struct nvme_rdma_device *dev;
434: struct ib_device *ibdev;
435:
436: if (!test_and_clear_bit(NVME_RDMA_Q_TR_READY, &queue->flags))
437: return;
438:
439: dev = queue->device;
440: ibdev = dev->dev;
441:
442: if (queue->pi_support)
443: ib_mr_pool_destroy(queue->qp, &queue->qp->sig_mrs);
444: ib_mr_pool_destroy(queue->qp, &queue->qp->rdma_mrs);
445:
446: /*
447: * The cm_id object might have been destroyed during RDMA connection
448: * establishment error flow to avoid getting other cma events, thus
449: * the destruction of the QP shouldn't use rdma_cm API.
450: */
451: ib_destroy_qp(queue->qp);
452: nvme_rdma_free_cq(queue);
453:
454: nvme_rdma_free_ring(ibdev, queue->rsp_ring, queue->queue_size,
455: sizeof(struct nvme_completion), DMA_FROM_DEVICE);
456:
457: nvme_rdma_dev_put(dev);
458: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 136/2 |
2026/08/25 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 87398
output: 46
thoughts: 86
Thoughts: Here's my thought process, as if I were analyzing this code:
**Thinking Through NVMe-RDMA Queue Destruction**
Okay, hold on a second! I need to re-examine the queue destruction process, specifically focusing on `nvme_rdma_destroy_queue_ib`. My initial understanding might be off, so I'm going to carefully walk through it again. Right, it checks `test_and_clear_bit(NVME_RDMA_Q_TR_READY, &queue->flags)`. That's the crucial point! The code *immediately* bails out if `NVME_RDMA_Q_TR_READY` isn't set. So, if that bit's clear, the queue destruction routine simply returns before doing anything! This is a conditional bail-out point, and its behavior hinges on that flag.
Therefore, I absolutely need to revisit `nvme_rdma_create_queue_ib` and understand *when* and *how* that `NVME_RDMA_Q_TR_READY` bit is being set. It's the critical link between queue creation and queue destruction, and I need to ensure it's handled properly to avoid any potential resource leaks or other problems. The creation routine is the only place it can be enabled, so let's start there.
|
| 137/2 |
2026/08/25 01:38 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_create_queue_ib]
Results: map[SourceCode: 499: static int nvme_rdma_create_queue_ib(struct nvme_rdma_queue *queue)
500: {
501: struct ib_device *ibdev;
502: const int send_wr_factor = 3; /* MR, SEND, INV */
503: const int cq_factor = send_wr_factor + 1; /* + RECV */
504: int ret, pages_per_mr;
505:
506: queue->device = nvme_rdma_find_get_device(queue->cm_id);
507: if (!queue->device) {
508: dev_err(queue->cm_id->device->dev.parent,
509: "no client data found!\n");
510: return -ECONNREFUSED;
511: }
512: ibdev = queue->device->dev;
513:
514: /* +1 for ib_drain_qp */
515: queue->cq_size = cq_factor * queue->queue_size + 1;
516:
517: ret = nvme_rdma_create_cq(ibdev, queue);
518: if (ret)
519: goto out_put_dev;
520:
521: ret = nvme_rdma_create_qp(queue, send_wr_factor);
522: if (ret)
523: goto out_destroy_ib_cq;
524:
525: queue->rsp_ring = nvme_rdma_alloc_ring(ibdev, queue->queue_size,
526: sizeof(struct nvme_completion), DMA_FROM_DEVICE);
527: if (!queue->rsp_ring) {
528: ret = -ENOMEM;
529: goto out_destroy_qp;
530: }
531:
532: /*
533: * Currently we don't use SG_GAPS MR's so if the first entry is
534: * misaligned we'll end up using two entries for a single data page,
535: * so one additional entry is required.
536: */
537: pages_per_mr = nvme_rdma_get_max_fr_pages(ibdev, queue->pi_support) + 1;
538: ret = ib_mr_pool_init(queue->qp, &queue->qp->rdma_mrs,
539: queue->queue_size,
540: IB_MR_TYPE_MEM_REG,
541: pages_per_mr, 0);
542: if (ret) {
543: dev_err(queue->ctrl->ctrl.device,
544: "failed to initialize MR pool sized %d for QID %d\n",
545: queue->queue_size, nvme_rdma_queue_idx(queue));
546: goto out_destroy_ring;
547: }
548:
549: if (queue->pi_support) {
550: ret = ib_mr_pool_init(queue->qp, &queue->qp->sig_mrs,
551: queue->queue_size, IB_MR_TYPE_INTEGRITY,
552: pages_per_mr, pages_per_mr);
553: if (ret) {
554: dev_err(queue->ctrl->ctrl.device,
555: "failed to initialize PI MR pool sized %d for QID %d\n",
556: queue->queue_size, nvme_rdma_queue_idx(queue));
557: goto out_destroy_mr_pool;
558: }
559: }
560:
561: set_bit(NVME_RDMA_Q_TR_READY, &queue->flags);
562:
563: return 0;
564:
565: out_destroy_mr_pool:
566: ib_mr_pool_destroy(queue->qp, &queue->qp->rdma_mrs);
567: out_destroy_ring:
568: nvme_rdma_free_ring(ibdev, queue->rsp_ring, queue->queue_size,
569: sizeof(struct nvme_completion), DMA_FROM_DEVICE);
570: out_destroy_qp:
571: rdma_destroy_qp(queue->cm_id);
572: out_destroy_ib_cq:
573: nvme_rdma_free_cq(queue);
574: out_put_dev:
575: nvme_rdma_dev_put(queue->device);
576: return ret;
577: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 138/2 |
2026/08/25 01:38 |
llm |
expert |
0m
Model:
Tokens:
input: 89192
output: 59
thoughts: 778
Thoughts: **Analyzing Potential Resource Leaks in the NVMe-oF RDMA Queue Creation and Destruction**
Okay, let's break this down systematically. My initial concern was about resource leaks during queue creation and destruction, specifically focusing on the `NVME_RDMA_Q_TR_READY` flag and the lifecycle of RDMA resources.
First, I see the typical flow: `nvme_rdma_create_queue_ib` is successful, it sets the `NVME_RDMA_Q_TR_READY` flag. Then, `nvme_rdma_addr_resolved` is called, which attempts `rdma_resolve_route`. If `rdma_resolve_route` fails, it *correctly* calls `nvme_rdma_destroy_queue_ib`. Critically, `nvme_rdma_destroy_queue_ib` checks and clears `NVME_RDMA_Q_TR_READY` before freeing resources using `test_and_clear_bit`. Since `NVME_RDMA_Q_TR_READY` *was* set by a successful `nvme_rdma_create_queue_ib`, this path ensures proper cleanup. No leak here.
Then, there's another path to consider. What if `nvme_rdma_wait_for_cm` fails, say during the connection establishment phase? The code calls `nvme_rdma_destroy_queue_ib`. Again, if `NVME_RDMA_Q_TR_READY` was set (meaning we got past address resolution successfully), the clear/free logic in `nvme_rdma_destroy_queue_ib` is executed. If it *wasn't* set (because address resolution didn't happen, or failed), the `test_and_clear_bit` handles the return early and the free is skipped. Seems correct.
Now, I'm thinking about double-frees. But the use of `test_and_clear_bit` in `nvme_rdma_destroy_queue_ib` is a key protection here. It guarantees that the cleanup only happens *once*, even if the destruction function is called multiple times due to error handling.
However, I need to check for other calls to `ib_cq_pool_get`. I'm going to re-examine the `nvmet_rdma_create_queue_ib` code. I'm looking for where `ib_cq_pool_get` is called. It seems this happens when the device is initialized and the queue is created. I have to look for all the calls to `nvmet_rdma_create_queue_ib` in the driver to check for a leak.
|
| 139/2 |
2026/08/25 01:39 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c IncludeSnippetLines:0 Name:nvmet_rdma_create_queue_ib SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_rdma_alloc_queue SourceFile:drivers/nvme/target/rdma.c SourceLine:1527]] TruncatedOutput:false]
|
| 140/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 90690
output: 44
thoughts: 15
Thoughts: Here's a summary of my thought process, as requested:
**Diving into `nvmet_rdma_alloc_queue`**
Okay, time to get into this. I'm going to start by taking a look at `nvmet_rdma_alloc_queue`. This function is critical, it's the gatekeeper, deciding if we can establish a queue or not. It's essentially the resource allocator. I need to understand its behavior thoroughly β what resources it's looking at, what constraints it's enforcing, and what failure paths it might present. I'll need to remember the underlying RDMA context. I want to know where it's being called from and the conditions in which that's happening. I know the general flow, but I need to zoom in and examine all the details to ensure optimal performance. My goal is to know how robust and efficient this is.
|
| 141/2 |
2026/08/25 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_alloc_queue]
Results: map[SourceCode:1453: static struct nvmet_rdma_queue *
1454: nvmet_rdma_alloc_queue(struct nvmet_rdma_device *ndev,
1455: struct rdma_cm_id *cm_id,
1456: struct rdma_cm_event *event)
1457: {
1458: struct nvmet_rdma_port *port = cm_id->context;
1459: struct nvmet_rdma_queue *queue;
1460: int ret;
1461:
1462: queue = kzalloc_obj(*queue);
1463: if (!queue) {
1464: ret = NVME_RDMA_CM_NO_RSC;
1465: goto out_reject;
1466: }
1467:
1468: nvmet_cq_init(&queue->nvme_cq);
1469: ret = nvmet_sq_init(&queue->nvme_sq, &queue->nvme_cq);
1470: if (ret) {
1471: ret = NVME_RDMA_CM_NO_RSC;
1472: goto out_free_queue;
1473: }
1474:
1475: ret = nvmet_rdma_parse_cm_connect_req(&event->param.conn, queue);
1476: if (ret)
1477: goto out_destroy_sq;
1478:
1479: /*
1480: * Schedules the actual release because calling rdma_destroy_id from
1481: * inside a CM callback would trigger a deadlock. (great API design..)
1482: */
1483: INIT_WORK(&queue->release_work, nvmet_rdma_release_queue_work);
1484: queue->dev = ndev;
1485: queue->cm_id = cm_id;
1486: queue->port = port->nport;
1487:
1488: spin_lock_init(&queue->state_lock);
1489: queue->state = NVMET_RDMA_Q_CONNECTING;
1490: INIT_LIST_HEAD(&queue->rsp_wait_list);
1491: INIT_LIST_HEAD(&queue->rsp_wr_wait_list);
1492: spin_lock_init(&queue->rsp_wr_wait_lock);
1493: INIT_LIST_HEAD(&queue->queue_list);
1494:
1495: queue->idx = ida_alloc(&nvmet_rdma_queue_ida, GFP_KERNEL);
1496: if (queue->idx < 0) {
1497: ret = NVME_RDMA_CM_NO_RSC;
1498: goto out_destroy_sq;
1499: }
1500:
1501: /*
1502: * Spread the io queues across completion vectors,
1503: * but still keep all admin queues on vector 0.
1504: */
1505: queue->comp_vector = !queue->host_qid ? 0 :
1506: queue->idx % ndev->device->num_comp_vectors;
1507:
1508:
1509: ret = nvmet_rdma_alloc_rsps(queue);
1510: if (ret) {
1511: ret = NVME_RDMA_CM_NO_RSC;
1512: goto out_ida_remove;
1513: }
1514:
1515: if (ndev->srqs) {
1516: queue->nsrq = ndev->srqs[queue->comp_vector % ndev->srq_count];
1517: } else {
1518: queue->cmds = nvmet_rdma_alloc_cmds(ndev,
1519: queue->recv_queue_size,
1520: !queue->host_qid);
1521: if (IS_ERR(queue->cmds)) {
1522: ret = NVME_RDMA_CM_NO_RSC;
1523: goto out_free_responses;
1524: }
1525: }
1526:
1527: ret = nvmet_rdma_create_queue_ib(queue);
1528: if (ret) {
1529: pr_err("%s: creating RDMA queue failed (%d).\n",
1530: __func__, ret);
1531: ret = NVME_RDMA_CM_NO_RSC;
1532: goto out_free_cmds;
1533: }
1534:
1535: return queue;
1536:
1537: out_free_cmds:
1538: if (!queue->nsrq) {
1539: nvmet_rdma_free_cmds(queue->dev, queue->cmds,
1540: queue->recv_queue_size,
1541: !queue->host_qid);
1542: }
1543: out_free_responses:
1544: nvmet_rdma_free_rsps(queue);
1545: out_ida_remove:
1546: ida_free(&nvmet_rdma_queue_ida, queue->idx);
1547: out_destroy_sq:
1548: nvmet_sq_destroy(&queue->nvme_sq);
1549: out_free_queue:
1550: nvmet_cq_put(&queue->nvme_cq);
1551: kfree(queue);
1552: out_reject:
1553: nvmet_rdma_cm_reject(cm_id, ret);
1554: return NULL;
1555: }
SourceFile:drivers/nvme/target/rdma.c]
|
| 142/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 92565
output: 57
thoughts: 37
Thoughts: **My Analytical Journey Through `nvmet_rdma_create_queue_ib`**
Okay, so I'm digging into this `nvmet_rdma_create_queue_ib` function, and the first thing that jumps out is its success path: it returns a `queue` if everything goes according to plan. That's standard practice, a clear signal of allocation and initialization. Now, the real question that's forming in my mind is, *where* does the allocation actually happen? I need to trace back the resource acquisition. If `nvmet_rdma_create_queue_ib` creates the queue, I need to understand where the memory is allocated. It must be allocated somewhere. The function signature is suggestive. The next logical step is to pinpoint the caller of `nvmet_rdma_alloc_queue`. Identifying the call site(s) of that allocation function is paramount to understanding the whole lifecycle of this queue, including its initialization, use, and deallocation. This is the key to understanding the allocation strategy and resource management. I'll need to know whether it's a static allocation, dynamic, pool-based, etc., to fully grasp the potential implications for performance, resource contention, and error handling.
|
| 143/2 |
2026/08/25 01:39 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c IncludeSnippetLines:0 Name:nvmet_rdma_alloc_queue SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:nvmet_rdma_queue_connect SourceFile:drivers/nvme/target/rdma.c SourceLine:1614]] TruncatedOutput:false]
|
| 144/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 92997
output: 44
thoughts: 15
Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:
**Diving into `nvmet_rdma_queue_connect`**
Alright, time to dive into `nvmet_rdma_queue_connect`. This is a critical function, no doubt. Understanding the intricacies of how an NVMe-oF target establishes RDMA connections is paramount. I need to refresh my mental model of how it handles the initial handshake, resource allocation, and any potential error conditions during the connection establishment phase. I'm thinking about the data structures involved: the queue pairs, the CQEs, the registrations... and what happens if something goes sideways β a network hiccup, a resource exhaustion issue, a mismatch in configuration. I'm mentally tracing the call flow, step-by-step, making sure I understand the locking mechanisms and any potential race conditions that might exist. Specifically, I need to look at how it deals with the different transport services and what RDMA verbs it uses and why. I'm keen to spot any potential performance bottlenecks or subtle flaws that could be exploited. Let's see if there are any gotchas in the code!
|
| 145/2 |
2026/08/25 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_queue_connect]
Results: map[SourceCode:1601: static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
1602: struct rdma_cm_event *event)
1603: {
1604: struct nvmet_rdma_device *ndev;
1605: struct nvmet_rdma_queue *queue;
1606: int ret = -EINVAL;
1607:
1608: ndev = nvmet_rdma_find_get_device(cm_id);
1609: if (!ndev) {
1610: nvmet_rdma_cm_reject(cm_id, NVME_RDMA_CM_NO_RSC);
1611: return -ECONNREFUSED;
1612: }
1613:
1614: queue = nvmet_rdma_alloc_queue(ndev, cm_id, event);
1615: if (!queue) {
1616: ret = -ENOMEM;
1617: goto put_device;
1618: }
1619:
1620: if (queue->host_qid == 0) {
1621: struct nvmet_rdma_queue *q;
1622: int pending = 0;
1623:
1624: /* Check for pending controller teardown */
1625: mutex_lock(&nvmet_rdma_queue_mutex);
1626: list_for_each_entry(q, &nvmet_rdma_queue_list, queue_list) {
1627: if (q->nvme_sq.ctrl == queue->nvme_sq.ctrl &&
1628: q->state == NVMET_RDMA_Q_DISCONNECTING)
1629: pending++;
1630: }
1631: mutex_unlock(&nvmet_rdma_queue_mutex);
1632: if (pending > NVMET_RDMA_BACKLOG) {
1633: ret = NVME_SC_CONNECT_CTRL_BUSY;
1634: goto put_device;
1635: }
1636: }
1637:
1638: ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);
1639: if (ret) {
1640: /*
1641: * Don't destroy the cm_id in free path, as we implicitly
1642: * destroy the cm_id here with non-zero ret code.
1643: */
1644: queue->cm_id = NULL;
1645: goto free_queue;
1646: }
1647:
1648: mutex_lock(&nvmet_rdma_queue_mutex);
1649: list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list);
1650: mutex_unlock(&nvmet_rdma_queue_mutex);
1651:
1652: return 0;
1653:
1654: free_queue:
1655: nvmet_rdma_free_queue(queue);
1656: put_device:
1657: kref_put(&ndev->ref, nvmet_rdma_free_dev);
1658:
1659: return ret;
1660: }
SourceFile:drivers/nvme/target/rdma.c]
|
| 146/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 94261
output: 44
thoughts: 59
Thoughts: Okay, let's break this down.
**Diving Deep into RDMA Connection Management and Queue Release**
Hmm, so `nvmet_rdma_cm_accept` failed, interesting. The code's behavior here is the critical part - it appears this failure triggers a cleanup sequence. It's setting `queue->cm_id` to `NULL`, which is standard practice for releasing resources associated with a connection identifier, then it's calling `nvmet_rdma_free_queue(queue)`. That function is clearly the next place to investigate for a comprehensive understanding. Let's dig into `nvmet_rdma_free_queue` to see what resources are actually being freed, how the queue itself is deallocated, and if there are any potential race conditions or other edge cases I need to be aware of. What are the dependencies? Is the queue being handled correctly regarding locking and reference counting to avoid double frees or memory leaks? I need to check those details.
|
| 147/2 |
2026/08/25 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_free_queue]
Results: map[SourceCode:1379: static void nvmet_rdma_free_queue(struct nvmet_rdma_queue *queue)
1380: {
1381: pr_debug("freeing queue %d\n", queue->idx);
1382:
1383: nvmet_sq_destroy(&queue->nvme_sq);
1384: nvmet_cq_put(&queue->nvme_cq);
1385:
1386: nvmet_rdma_destroy_queue_ib(queue);
1387: if (!queue->nsrq) {
1388: nvmet_rdma_free_cmds(queue->dev, queue->cmds,
1389: queue->recv_queue_size,
1390: !queue->host_qid);
1391: }
1392: nvmet_rdma_free_rsps(queue);
1393: ida_free(&nvmet_rdma_queue_ida, queue->idx);
1394: kfree(queue);
1395: }
SourceFile:drivers/nvme/target/rdma.c]
|
| 148/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 94885
output: 44
thoughts: 1498
Thoughts: **Vulnerability Assessment: Leaked RDMA Queue in nvmet-rdma**
Okay, let's break this down. First, I'm looking at `nvmet_rdma_destroy_queue_ib` and confirming that it correctly handles the cleanup of the RDMA queue by destroying the QP and putting the CQ back in the pool. That seems straightforward. Then, I remember I had seen a warning in `ib_cq_pool_cleanup` and try to trace where that may have come from.
Now I'm diving into the `nvmet_rdma_queue_connect` function and the conditional block dealing with `queue->host_qid == 0`. I see a crucial check: `if (pending > NVMET_RDMA_BACKLOG)`. This check is designed to prevent a backlog of disconnect operations. However, if this check fails (meaning `pending` is greater than the backlog limit), the code *jumps* directly to `put_device`, bypassing the `free_queue` label. Wait, and `free_queue` calls `nvmet_rdma_free_queue`!
Aha! This means the allocated `queue` structure, the CQ, and the QP are never freed, and the CQ count in the pool is never decremented, leading to a memory leak. And the `queue` structure contains the `cm_id` as well.
The implications are serious. This perfectly explains the warning I saw earlier, where the CQ pool cleanup might not have returned the resources.
Now the question is, how exploitable is this? This is a denial-of-service vulnerability, specifically a memory leak.
Since `nvmet-rdma` is a NVMe over Fabrics target, an attacker needs to be able to connect to the target. This is possible through a reachable network interface (like a TCP port for RoCE/iWARP). An attacker can trigger this leak by sending RDMA CM connect requests to the target port. The attacker needs to send a connect request, then disconnect, and send a new connect request while it's disconnecting.
By sending many connect requests, the attacker can cause `pending > NVMET_RDMA_BACKLOG` to be true. This happens when there's an increasing number of pending disconnects. Then, the next connect request will hit the leak and `queue` won't be freed.
Since the RDMA CM takes care of the `cm_id`, it is not an issue since it gets cleaned up by RDMA CM if `nvmet_rdma_queue_connect` returns a non-zero value. However, the leak remains because the QPs are not being cleaned up, either.
This is a **remote Denial of Service** vulnerability because any unprivileged user who can reach the NVMe-oF target port can trigger this, potentially leading to a system crash or performance degradation due to memory exhaustion.
|
| 149/2 |
2026/08/25 01:39 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/target/rdma.c Name:nvmet_rdma_remove_one]
Results: map[SourceCode:2088: static void nvmet_rdma_remove_one(struct ib_device *ib_device, void *client_data)
2089: {
2090: struct nvmet_rdma_queue *queue, *tmp;
2091: struct nvmet_rdma_device *ndev;
2092: bool found = false;
2093:
2094: mutex_lock(&device_list_mutex);
2095: list_for_each_entry(ndev, &device_list, entry) {
2096: if (ndev->device == ib_device) {
2097: found = true;
2098: break;
2099: }
2100: }
2101: mutex_unlock(&device_list_mutex);
2102:
2103: if (!found)
2104: return;
2105:
2106: /*
2107: * IB Device that is used by nvmet controllers is being removed,
2108: * delete all queues using this device.
2109: */
2110: mutex_lock(&nvmet_rdma_queue_mutex);
2111: list_for_each_entry_safe(queue, tmp, &nvmet_rdma_queue_list,
2112: queue_list) {
2113: if (queue->dev->device != ib_device)
2114: continue;
2115:
2116: pr_info("Removing queue %d\n", queue->idx);
2117: list_del_init(&queue->queue_list);
2118: __nvmet_rdma_queue_disconnect(queue);
2119: }
2120: mutex_unlock(&nvmet_rdma_queue_mutex);
2121:
2122: flush_workqueue(nvmet_wq);
2123: flush_workqueue(nvmet_aen_wq);
2124: }
SourceFile:drivers/nvme/target/rdma.c]
|
| 150/2 |
2026/08/25 01:39 |
llm |
expert |
0m
Model:
Tokens:
input: 97619
output: 44
thoughts: 519
Thoughts: **Analyzing a Persistent Memory Leak in NVMe-oF RDMA**
Okay, so I've been digging into this `nvmet_rdma_remove_one` function, and something's clearly off. The problem is that the `queue` in question is supposed to be added to the `nvmet_rdma_queue_list` using `list_add_tail` *after* the error check. That's a huge red flag because if an error occurs *before* that point, the `queue` never gets added to the list. That means `nvmet_rdma_remove_one` will never touch it during cleanup. This means the associated QP and CQ are just left hanging.
Now, things get a bit more convoluted. The `disable_device` function calls `ib_cq_pool_cleanup`, which initially looks like it might catch this. It checks if `cq->cqe_used` is non-zero and prints a warning. Fine. But, here's the kicker: `ib_cq_pool_cleanup` calls `ib_free_cq(cq)`. However, `ib_free_cq` has a `WARN_ON_ONCE(cq->cqe_used)` check. So, when the warning is triggered, the function just returns early! It doesn't actually free the CQ! Meaning it's also leaked, along with the QP and the original `queue` structure itself. We have a solid memory leak here.
So, the question is, how bad is this? It's a leak, which is bad, but is it exploitable beyond a denial-of-service? Probably not, since we're just talking about memory exhaustion. And, can an attacker trigger this? Yes, if they can send RDMA CM connect requests to the NVMe-oF target port, which triggers the faulty code path when the errors occur.
Now, the interesting bit is if syzbot can actually trigger this. It looks like syzbot *can* configure the NVMe-oF target through configfs if it has root access in `init_net`. And if that's the case, then yes, it could trigger this bug by sending those connect requests.
Wait a second, there's a syzbot report that says "smbdirect: ib_dev[syz1] removed." That suggests `smbdirect` was also loaded. The bug is in `nvmet-rdma`, and the fact it could be triggered by `smbdirect` would be very useful in reproducing the leak.
I wonder if there are any other places with a similar leak? Let's check `nvme_rdma_alloc_queue` on the host side, just to be sure...
|
| 151/2 |
2026/08/25 01:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/nvme/host/rdma.c Name:nvme_rdma_alloc_queue]
Results: map[SourceCode: 579: static int nvme_rdma_alloc_queue(struct nvme_rdma_queue *queue)
580: {
581: struct nvme_rdma_ctrl *ctrl = queue->ctrl;
582: int idx = nvme_rdma_queue_idx(queue);
583: struct sockaddr *src_addr = NULL;
584: int ret;
585:
586: mutex_init(&queue->queue_lock);
587: if (idx && ctrl->ctrl.max_integrity_segments)
588: queue->pi_support = true;
589: else
590: queue->pi_support = false;
591: init_completion(&queue->cm_done);
592:
593: if (idx > 0)
594: queue->cmnd_capsule_len = ctrl->ctrl.ioccsz * 16;
595: else
596: queue->cmnd_capsule_len = sizeof(struct nvme_command);
597:
598: queue->cm_id = rdma_create_id(&init_net, nvme_rdma_cm_handler, queue,
599: RDMA_PS_TCP, IB_QPT_RC);
600: if (IS_ERR(queue->cm_id)) {
601: dev_info(ctrl->ctrl.device,
602: "failed to create CM ID: %ld\n", PTR_ERR(queue->cm_id));
603: ret = PTR_ERR(queue->cm_id);
604: goto out_destroy_mutex;
605: }
606:
607: if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR)
608: src_addr = (struct sockaddr *)&ctrl->src_addr;
609:
610: queue->cm_error = -ETIMEDOUT;
611: ret = rdma_resolve_addr(queue->cm_id, src_addr,
612: (struct sockaddr *)&ctrl->addr,
613: NVME_RDMA_CM_TIMEOUT_MS);
614: if (ret) {
615: dev_info(ctrl->ctrl.device,
616: "rdma_resolve_addr failed (%d).\n", ret);
617: goto out_destroy_cm_id;
618: }
619:
620: ret = nvme_rdma_wait_for_cm(queue);
621: if (ret) {
622: dev_info(ctrl->ctrl.device,
623: "rdma connection establishment failed (%d)\n", ret);
624: goto out_destroy_cm_id;
625: }
626:
627: set_bit(NVME_RDMA_Q_ALLOCATED, &queue->flags);
628:
629: return 0;
630:
631: out_destroy_cm_id:
632: rdma_destroy_id(queue->cm_id);
633: nvme_rdma_destroy_queue_ib(queue);
634: out_destroy_mutex:
635: mutex_destroy(&queue->queue_lock);
636: return ret;
637: }
SourceFile:drivers/nvme/host/rdma.c]
|
| 152/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 99746
output: 32
thoughts: 181
Thoughts: **RDMA Queue Destruction and SMBDirect Integration**
Okay, so I'm looking at this `nvme_rdma_wait_for_cm` function. If it fails, as expected, it calls `nvme_rdma_destroy_queue_ib`. That seems like the right thing to do. And if `NVME_RDMA_Q_TR_READY` was set, then it frees up the resources. Seems logical, everything checks out there.
Now, what about `smbdirect`? I'm curious how it handles similar queue destruction scenarios. Oh, right, I remember checking that. `smbdirect` uses `ib_alloc_cq_any`. But hang on... `ib_alloc_cq_any` calls `__ib_alloc_cq_any`, which *then* calls `__ib_alloc_cq`. Now, does `__ib_alloc_cq` add the CQ to `dev->cq_pools`? Let me see... Nope, I verified that earlier. Okay, I was missing something.
Hmm, what if, hypothetically, even though I don't think it is, that `smbdirect` *does* actually use `ib_cq_pool_get` somewhere? I should probably double check that just to be thorough and remove that thought.
|
| 153/2 |
2026/08/25 01:40 |
tool |
grepper |
0m
Error:
Args: map[Expression:ib_cq_pool_get PathPrefix:fs/smb/]
Results:
|
| 154/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 100254
output: 46
thoughts: 1494
Thoughts: **Analysis of a Potential Memory Leak in NVMe-oF RDMA Code**
Okay, so I'm looking at this warning triggered by syzbot, and my initial thought is that it's probably *not* directly related to `smbdirect`. The message seems to be complaining about a leaked completion queue (CQ), but if `smbdirect` were the source, the warning would likely point to `dev->cq_pools` directly, which it doesn't. And I remember `ib_cq_pool_cleanup` specifically iterates through `dev->cq_pools` to check for this condition. My understanding is that `smbdirect` doesn't use shared CQs, so it's probably just a bystander, a module that was loaded when the device went down. The actual leak likely lies within one of the ULPs (Upper Layer Protocols) that *do* use shared CQs. Since syzbot triggered it, and it tests multiple things, it could be `nvmet-rdma`, `nvme-rdma`, `iser`, `isert`, `rtrs`, or `srpt`.
I did a global grep for `ib_cq_pool_get`, and I only found those specific ULPs. Is there any other way `cqe_used` can be modified? No.
Let's focus on `nvmet_rdma_queue_connect`. I'm starting to think this is where the leak is. I see a potential issue if `queue->host_qid == 0` and `pending > NVMET_RDMA_BACKLOG`. The code locks a mutex and checks a list of pending disconnecting queues. If there are too many, it returns an error with `NVME_SC_CONNECT_CTRL_BUSY`, and then it goes to `put_device`, which reduces the ref count for `ndev` and returns an error code, but **it doesn't free the `queue` itself**. This is a classic leak. And the `queue` structure contains the `queue->cq` which was allocated by `ib_cq_pool_get`. So the `cqe_used` count is leaked.
This looks like a pretty clear 100% memory leak. Now, who can trigger it?
The attacker needs to send RDMA CM connect requests. These requests come over the network (RoCE, iWARP, InfiniBand). If the NVMe-oF target is exposed, any remote attacker can send these. So it's remotely triggerable. And locally? Yes, even an unprivileged process can use `rdma_ucm` to send connect requests to `127.0.0.1` or the local IP address. The `rdma_ucm` device node `/dev/infiniband/rdma_cm` is usually accessible to the `rdma` group, or sometimes world-rw. Even without `rdma_ucm`, if an attacker has `CAP_NET_RAW` they can send raw packets. Since RDMA is network based, it's definitely remotely triggerable. Therefore, `RemoteTrigger: true`, `NetworkTrigger: true`, and it's definitely a `DenialOfService` (a memory leak). However, it's not directly `Exploitable` in terms of memory corruption or use-after-free, it's just a memory leak.
Let's double-check other error paths in `nvmet_rdma_alloc_queue`.
It calls `nvmet_rdma_create_queue_ib`, and, on error, it jumps to `out_free_cmds` path and looks correct.
What about `nvmet_rdma_cm_accept` failure? It calls `nvmet_rdma_free_queue`. That looks correct too.
So, the only leak I'm seeing is that specific `pending > NVMET_RDMA_BACKLOG` check in `nvmet_rdma_queue_connect`.
Let's check `iser` for any similar leaks.
|
| 155/2 |
2026/08/25 01:40 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/infiniband/ulp/iser/iser_verbs.c Name:iser_cma_handler]
Results: map[SourceCode: 671: static int iser_cma_handler(struct rdma_cm_id *cma_id,
672: struct rdma_cm_event *event)
673: {
674: struct iser_conn *iser_conn;
675: int ret = 0;
676:
677: iser_conn = cma_id->context;
678: iser_info("%s (%d): status %d conn %p id %p\n",
679: rdma_event_msg(event->event), event->event,
680: event->status, cma_id->context, cma_id);
681:
682: mutex_lock(&iser_conn->state_mutex);
683: switch (event->event) {
684: case RDMA_CM_EVENT_ADDR_RESOLVED:
685: iser_addr_handler(cma_id);
686: break;
687: case RDMA_CM_EVENT_ROUTE_RESOLVED:
688: iser_route_handler(cma_id);
689: break;
690: case RDMA_CM_EVENT_ESTABLISHED:
691: iser_connected_handler(cma_id, event->param.conn.private_data);
692: break;
693: case RDMA_CM_EVENT_REJECTED:
694: iser_info("Connection rejected: %s\n",
695: rdma_reject_msg(cma_id, event->status));
696: fallthrough;
697: case RDMA_CM_EVENT_ADDR_ERROR:
698: case RDMA_CM_EVENT_ROUTE_ERROR:
699: case RDMA_CM_EVENT_CONNECT_ERROR:
700: case RDMA_CM_EVENT_UNREACHABLE:
701: iser_connect_error(cma_id);
702: break;
703: case RDMA_CM_EVENT_DISCONNECTED:
704: case RDMA_CM_EVENT_ADDR_CHANGE:
705: case RDMA_CM_EVENT_TIMEWAIT_EXIT:
706: iser_cleanup_handler(cma_id, false);
707: break;
708: case RDMA_CM_EVENT_DEVICE_REMOVAL:
709: /*
710: * we *must* destroy the device as we cannot rely
711: * on iscsid to be around to initiate error handling.
712: * also if we are not in state DOWN implicitly destroy
713: * the cma_id.
714: */
715: iser_cleanup_handler(cma_id, true);
716: if (iser_conn->state != ISER_CONN_DOWN) {
717: iser_conn->ib_conn.cma_id = NULL;
718: ret = 1;
719: }
720: break;
721: default:
722: iser_err("Unexpected RDMA CM event: %s (%d)\n",
723: rdma_event_msg(event->event), event->event);
724: break;
725: }
726: mutex_unlock(&iser_conn->state_mutex);
727:
728: return ret;
729: }
SourceFile:drivers/infiniband/ulp/iser/iser_verbs.c]
|
| 156/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 103654
output: 46
thoughts: 18
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Initiating the Analysis: The "Initiator" Perspective**
Okay, here we go. I'm the one kicking this whole thing off. The "initiator," if you will. The goal is to understand the interaction, the flow, the state.
My immediate focus is on the *target* side of the interaction. Let's start by looking at `isert`. Knowing you, that's what you probably want to know. It's the key to understanding the data flow here. Let's see what we can find.
|
| 157/2 |
2026/08/25 01:40 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_cma_handler]
Results: map[SourceCode: SourceFile:]
|
| 158/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 103871
output: 45
thoughts: 11
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Diving into `insert_connect_request`**
Alright, time to dig into this. Let's see... the code's got me thinking, it's pretty clear where we're at now. So, my mind went straight to examining the `insert_connect_request` function. Gotta make sure everything's firing on all cylinders in that section. It's a critical piece, and I want to confirm it is running well. We need a solid understanding of this function to maintain a smooth system. Right, let's get started.
|
| 159/2 |
2026/08/25 01:40 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_connect_request]
Results: map[SourceCode: SourceFile:]
|
| 160/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 104092
output: 42
thoughts: 20
Thoughts: Here's a summary of my thought process, as requested:
**Checking the RDMA Event Handling**
Okay, time to dive into this. Given the context, my immediate thought is to zero in on `ib_isert.c`. That's where I'd expect to find the RDMA connection management event handling logic, specifically how the driver reacts to a `rdma_cm_event`. I need to understand how the system processes events that are triggered during an RDMA connection, so I'll start by examining that file closely. I need to understand the connection management flow and event processing. This is where I will start my investigation.
|
| 161/2 |
2026/08/25 01:40 |
tool |
grepper |
0m
Args: map[Expression:rdma_cm_event PathPrefix:drivers/infiniband/ulp/isert/ib_isert.c]
Results: map[Output:drivers/infiniband/ulp/isert/ib_isert.c=411=static int
drivers/infiniband/ulp/isert/ib_isert.c:412:isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
drivers/infiniband/ulp/isert/ib_isert.c-413-{
--
drivers/infiniband/ulp/isert/ib_isert.c=600=isert_np_cma_handler(struct isert_np *isert_np,
drivers/infiniband/ulp/isert/ib_isert.c:601: enum rdma_cm_event_type event)
drivers/infiniband/ulp/isert/ib_isert.c-602-{
--
drivers/infiniband/ulp/isert/ib_isert.c=627=isert_disconnected_handler(struct rdma_cm_id *cma_id,
drivers/infiniband/ulp/isert/ib_isert.c:628: enum rdma_cm_event_type event)
drivers/infiniband/ulp/isert/ib_isert.c-629-{
--
drivers/infiniband/ulp/isert/ib_isert.c=671=static int
drivers/infiniband/ulp/isert/ib_isert.c:672:isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
drivers/infiniband/ulp/isert/ib_isert.c-673-{
]
|
| 162/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 104698
output: 45
thoughts: 11
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Diving into `insert_connect_request`**
Alright, time to dig into this. Let's see... the code's got me thinking, it's pretty clear where we're at now. So, my mind went straight to examining the `insert_connect_request` function. Gotta make sure everything's firing on all cylinders in that section. It's a critical piece, and I want to confirm it is running well. We need a solid understanding of this function to maintain a smooth system. Right, let's get started.
|
| 163/2 |
2026/08/25 01:40 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/infiniband/ulp/isert/ib_isert.c Name:isert_connect_request]
Results: map[SourceCode: SourceFile:]
|
| 164/2 |
2026/08/25 01:40 |
llm |
expert |
0m
Model:
Tokens:
input: 104919
output: 45
thoughts: 12
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Navigating the Labyrinth: Identifying the Point**
Alright, let's see. My mental map is pretty clear on this project. Weβre deep into the code, and there was a specific issue we needed to resolve. Let me see where I am at, the context of the problem. Okay, it's not a generic issue that could be anywhere, so it should be around the area I had mapped out for the relevant module. Let me check the line number in the log files. Ah, yes, here we are! Line 412. Now I can pinpoint the exact code block that's causing the problem, or at the very least, where the investigation needs to start. Knowing the line number is crucial for the next steps - debugging, testing changes, and verifying fixes. That small detail is going to save a lot of time and effort. I can begin the debugging with the relevant context in mind. Now it is just a matter of digging through the code.
|
| 165/2 |
2026/08/25 01:41 |
tool |
read-file |
0m
Args: map[File:drivers/infiniband/ulp/isert/ib_isert.c FirstLine:411 LineCount:100]
Results: map[Contents: 411: static int
412: isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
413: {
414: struct isert_np *isert_np = cma_id->context;
415: struct iscsi_np *np = isert_np->np;
416: struct isert_conn *isert_conn;
417: struct isert_device *device;
418: int ret = 0;
419:
420: spin_lock_bh(&np->np_thread_lock);
421: if (!np->enabled) {
422: spin_unlock_bh(&np->np_thread_lock);
423: isert_dbg("iscsi_np is not enabled, reject connect request\n");
424: return rdma_reject(cma_id, NULL, 0, IB_CM_REJ_CONSUMER_DEFINED);
425: }
426: spin_unlock_bh(&np->np_thread_lock);
427:
428: isert_dbg("cma_id: %p, portal: %p\n",
429: cma_id, cma_id->context);
430:
431: isert_conn = kzalloc_obj(struct isert_conn);
432: if (!isert_conn)
433: return -ENOMEM;
434:
435: isert_init_conn(isert_conn);
436: isert_conn->cm_id = cma_id;
437:
438: device = isert_device_get(cma_id);
439: if (IS_ERR(device)) {
440: ret = PTR_ERR(device);
441: goto out;
442: }
443: isert_conn->device = device;
444:
445: ret = isert_alloc_login_buf(isert_conn, cma_id->device);
446: if (ret)
447: goto out_conn_dev;
448:
449: isert_set_nego_params(isert_conn, &event->param.conn);
450:
451: isert_conn->qp = isert_create_qp(isert_conn, cma_id);
452: if (IS_ERR(isert_conn->qp)) {
453: ret = PTR_ERR(isert_conn->qp);
454: goto out_rsp_dma_map;
455: }
456:
457: ret = isert_login_post_recv(isert_conn);
458: if (ret)
459: goto out_destroy_qp;
460:
461: ret = isert_rdma_accept(isert_conn);
462: if (ret)
463: goto out_destroy_qp;
464:
465: mutex_lock(&isert_np->mutex);
466: list_add_tail(&isert_conn->node, &isert_np->accepted);
467: mutex_unlock(&isert_np->mutex);
468:
469: return 0;
470:
471: out_destroy_qp:
472: isert_destroy_qp(isert_conn);
473: out_rsp_dma_map:
474: isert_free_login_buf(isert_conn);
475: out_conn_dev:
476: isert_device_put(device);
477: out:
478: kfree(isert_conn);
479: rdma_reject(cma_id, NULL, 0, IB_CM_REJ_CONSUMER_DEFINED);
480: return ret;
481: }
482:
483: static void
484: isert_connect_release(struct isert_conn *isert_conn)
485: {
486: struct isert_device *device = isert_conn->device;
487:
488: isert_dbg("conn %p\n", isert_conn);
489:
490: BUG_ON(!device);
491:
492: isert_free_rx_descriptors(isert_conn);
493: if (isert_conn->cm_id &&
494: !isert_conn->dev_removed)
495: rdma_destroy_id(isert_conn->cm_id);
496:
497: if (isert_conn->qp)
498: isert_destroy_qp(isert_conn);
499:
500: isert_unmap_tx_desc(&isert_conn->login_tx_desc, device->ib_device);
501:
502: if (isert_conn->login_desc)
503: isert_free_login_buf(isert_conn);
504:
505: isert_device_put(device);
506:
507: if (isert_conn->dev_removed)
508: wake_up_interruptible(&isert_conn->rem_wait);
509: else
510: kfree(isert_conn);
]
|
| 166/2 |
2026/08/25 01:41 |
llm |
expert |
0m
Model:
Tokens:
input: 106738
output: 66
thoughts: 970
Thoughts: **Analysis of an NVMe-oF RDMA Memory Leak**
Okay, let's break this down. My initial check of the `isert_connect_request` error paths and `isert_destroy_qp` didn't reveal any leaks. I was also satisfied that `nvme_rdma_alloc_queue` itself seemed clean. That points the finger directly at `nvmet_rdma_alloc_queue` when `pending` exceeds `NVMET_RDMA_BACKLOG`. This is a straight-up memory leak, leading to a Denial of Service. And it's network-exploitable, even by an unprivileged user, if they can reach the NVMe-oF target port, which is often the case for network services.
So, the classification is: Exploitable - False (memory leak), DenialOfService - True, Unprivileged - True, UserNamespace - True, VMGuestTrigger - False, VMHostTrigger - False, NetworkTrigger - True (RDMA over Ethernet/RoCE), RemoteTrigger - True (RDMA), PeripheralTrigger - False, FilesystemTrigger - False.
Wait, I need to confirm that `nvmet_rdma_alloc_queue` is *only* called from `nvmet_rdma_queue_connect`, which is the RDMA CM connect handler. Yes, it is. Definitely a network trigger.
Now, let's confirm the leak in `nvmet_rdma_alloc_queue`. Looking closely at the code... Yes, absolutely, it jumps to `put_device` when `pending > NVMET_RDMA_BACKLOG`, bypassing `free_queue`! That's a clear leak of the `queue` and everything it allocates: `queue->cq`, `queue->qp`, `queue->cmds`, `queue->rsp_ring`, etc. And since `queue->cq` uses a shared pool, `cqe_used` never gets decremented which causes the warning in `ib_cq_pool_cleanup`.
To trigger this, here's the attack vector:
1. Connect to the NVMe-oF target (admin queue, `host_qid == 0`).
2. Disconnect.
3. Repeat this quickly until the number of queues in `NVMET_RDMA_Q_DISCONNECTING` state exceeds `NVMET_RDMA_BACKLOG` (128).
4. The next connection attempt hits the error path, leaking the queue.
5. Unregister the RDMA device (e.g., removing the `siw` or `rxe` device, or just unloading the module).
6. The WARNING appears.
Since syzbot *can* create and destroy `siw` or `rxe` devices and send RDMA CM connect requests, it can absolutely trigger this. The bug leads to a memory leak, which can lead to OOM, making it a DoS. Itβs remotely triggerable if the NVMe-oF target is exposed on the network. I will update the findings accordingly.
|
| 167/2 |
2026/08/25 01:41 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:true PeripheralTrigger:false RemoteTrigger:true Unprivileged:true UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 168/2 |
2026/08/25 01:41 |
llm |
expert |
0m
Model:
Tokens:
input: 108500
output: 551
thoughts: 378
Thoughts: **My Analysis of the NVMe-oF RDMA Target Memory Leak**
Okay, the results are in. Time to solidify my understanding and write this up definitively. From what I can see, there's a nasty memory leak specifically within the `nvmet-rdma` target, and it's quite subtle. The crux of the issue boils down to how we're handling new connection requests for the admin queue β that's when `host_qid` is zero.
The `nvmet_rdma_queue_connect()` function is responsible, naturally, for connecting these admin queues and allocating resources. It calls `nvmet_rdma_alloc_queue()` to do the heavy lifting of allocating the queue. Here's where it gets interesting and where the leak happens. Inside `nvmet_rdma_alloc_queue()`, there's a check for controller teardowns β `pending > NVMET_RDMA_BACKLOG`. If that limit is exceeded, we have a problem.
The code then jumps to `put_device`, which returns an error. The crucial oversight is that the code *bypasses* the `free_queue` label entirely, which means that the freshly allocated `queue` structure and its associated resources, like the Queue Pair (QP), Completion Queue (CQ), and the command buffers, are never freed. They're justβ¦lost.
Worse still, because the CQ is pulled from a shared pool using `ib_cq_pool_get()`, the `cqe_used` counter is incremented, signaling that itβs in use. Since the queue is leaked, `ib_cq_pool_put()` is *never* called, leaving this counter perpetually incremented. Later, when the RDMA device is unregistered, the `ib_cq_pool_cleanup()` function gets called. This is when the leaked CQ is exposed because it checks if all shared CQs are unused. It's supposed to be unused (`cq->cqe_used`), but because of the leak, it's not. That's how we get the warning.
This is a clear memory leak vulnerability, and it's easily triggered by flooding the target port with RDMA CM connect/disconnect requests. You can essentially exceed the `BACKLOG` value. Since this happens remotely over the network, that makes it even more worrying; RoCE and iWARP are specifically mentioned as being susceptible. An unprivileged attacker could potentially exploit this to cause a Denial of Service (DoS) β basically, out-of-memory errors (OOM). The good news is that it *doesn't* lead to memory corruption or privilege escalation, so at least we can mitigate the impact to just a DoS.
|
| 169/1 |
2026/08/25 01:41 |
action |
format-explanation |
0m
Results: map[Explanation:The bug is a memory leak in the NVMe over Fabrics (NVMe-oF) RDMA target driver
(`nvmet-rdma`).
When a new RDMA CM connection request is received for an admin queue (`host_qid
== 0`), `nvmet_rdma_queue_connect()` allocates a new queue and its associated
RDMA resources using `nvmet_rdma_alloc_queue()`.
Inside `nvmet_rdma_alloc_queue()`, after allocating the Queue Pair (QP) and
Completion Queue (CQ), the code checks if there are too many pending controller
teardowns (`pending > NVMET_RDMA_BACKLOG`). If this limit is exceeded, the
function jumps to the `put_device` label to return an error:
```c
if (pending > NVMET_RDMA_BACKLOG) {
ret = NVME_SC_CONNECT_CTRL_BUSY;
goto put_device;
}
```
However, this jump skips the `free_queue` label, completely leaking the newly
allocated `queue` structure and all its RDMA resources (QP, CQ, command buffers,
etc.).
Because the CQ was allocated from a shared device pool using `ib_cq_pool_get()`,
its `cqe_used` counter was incremented. Since the queue is leaked,
`ib_cq_pool_put()` is never called to decrement the counter. Later, when the
RDMA device is unregistered (e.g., device removal or module unload),
`ib_cq_pool_cleanup()` verifies that all shared CQs are unused
(`WARN_ON(cq->cqe_used)`). The leaked CQ triggers this warning.
**Security Impact:**
* **Denial of Service (DoS):** An attacker can repeatedly trigger this error
path to leak kernel memory and RDMA resources, eventually leading to an
Out-Of-Memory (OOM) state or resource exhaustion.
* **Remote / Network Triggerable:** The vulnerability can be triggered remotely
by any unprivileged attacker who can reach the NVMe-oF target's RDMA port (e.g.,
over RoCE, iWARP, or InfiniBand) by sending a burst of connect and disconnect
requests to exceed the `NVMET_RDMA_BACKLOG` limit (which is 128).
* **Exploitability:** This is purely a memory leak. It does not result in memory
corruption (like use-after-free or out-of-bounds access) or privilege
escalation, so it is not exploitable beyond DoS.]
|