| 0/0 |
2026/06/01 05:21 |
flow |
assessment-security |
1h06m
Results: map[DenialOfService:true Explanation:Based on the analysis of the provided bug report and kernel source code, here is
the assessment of the security impact:
### Bug Mechanism
The syzkaller report shows multiple tasks blocked for over 143 seconds in
`__mutex_lock_common` while trying to acquire `nfsd_mutex` inside
`nfsd_nl_threads_set_doit`.
The root cause of this hang is a deadlock/livelock involving `nfsd` thread
shutdown and memory allocation failures.
When an `nfsd` thread processes a request, it allocates pages in
`svc_alloc_arg()`. If `alloc_page(GFP_KERNEL)` fails (which is common under
heavy load or OOM conditions generated by syzkaller), the thread sleeps for
500ms in `TASK_INTERRUPTIBLE` and retries indefinitely. It only breaks out of
this loop if `signalled()` or `kthread_should_stop()` is true.
However, `nfsd` threads block most signals and use a custom stopping mechanism
(`SP_NEED_VICTIM` flag checked via `svc_thread_should_stop(rqstp)`), meaning
`kthread_should_stop()` will never return true for them. Thus, the thread loops
forever.
When a user tries to reconfigure or shutdown the `nfsd` server (e.g., by sending
a netlink message to set the number of threads to 0),
`nfsd_nl_threads_set_doit()` acquires `nfsd_mutex` and eventually calls
`svc_stop_kthreads()`. `svc_stop_kthreads()` sets the `SP_NEED_VICTIM` flag and
waits for the `nfsd` threads to exit. Because the victim thread is stuck in the
`svc_alloc_arg()` infinite loop and never checks `svc_thread_should_stop()`, it
never exits.
Consequently, `svc_stop_kthreads()` hangs forever while holding `nfsd_mutex`.
Any subsequent tasks attempting to configure `nfsd` will block on `nfsd_mutex`
in `TASK_UNINTERRUPTIBLE` state, eventually triggering the hung task detector.
### Security Impact Assessment
* **Exploitable:** No.
This is a pure logic bug leading to a livelock/deadlock. It does not involve
memory corruption, use-after-free, or out-of-bounds accesses that could be
leveraged for privilege escalation or arbitrary code execution.
* **Denial Of Service:** Yes.
Triggering this bug causes the `nfsd` subsystem to hang permanently.
Furthermore, tasks blocked in `TASK_UNINTERRUPTIBLE` will trigger the kernel's
hung task panic (if `kernel.hung_task_panic` is enabled, which is common in many
production environments), leading to a complete system crash.
* **Accessible From Unprivileged Processes:** No.
The netlink command `NFSD_CMD_THREADS_SET` requires `GENL_ADMIN_PERM`, which
translates to the `CAP_NET_ADMIN` capability. A standard unprivileged process
does not have this capability.
* **Accessible From User Namespaces:** Yes.
The `nfsd` netlink family is registered with `.netnsok = true`. An unprivileged
user can create a new user namespace and a new network namespace, thereby
gaining `CAP_NET_ADMIN` within that namespace. From there, they can start an
`nfsd` instance, induce memory pressure, and send the netlink message to trigger
the deadlock.
* **VM Guest / Host / Network / Peripheral / Filesystem Triggers:** No.
This bug is strictly related to the local configuration of the NFS server via
netlink sockets and local memory pressure. It is not triggered by processing
remote network traffic, mounting filesystems, or interacting with peripherals/VM
boundaries. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/06/01 05:21 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#define BITMASK(bf_off,bf_len) (((1ull << (bf_len)) - 1) << (bf_off))
#define STORE_BY_BITMASK(type,htobe,addr,val,bf_off,bf_len) *(type*)(addr) = htobe((htobe(*(type*)(addr)) & ~BITMASK((bf_off), (bf_len))) | (((type)(val) << (bf_off)) & BITMASK((bf_off), (bf_len))))
uint64_t r[4] = {0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// socket arguments: [
// domain: socket_domain = 0x2a (8 bytes)
// type: socket_type = 0x2 (8 bytes)
// proto: int32 = 0x0 (4 bytes)
// ]
// returns sock
res = syscall(__NR_socket, /*domain=AF_QIPCRTR*/0x2aul, /*type=SOCK_DGRAM*/2ul, /*proto=*/0);
if (res != -1)
r[0] = res;
// getsockname$packet arguments: [
// fd: sock_packet (resource)
// addr: ptr[out, sockaddr_ll] {
// sockaddr_ll {
// sll_family: const = 0x11 (2 bytes)
// sll_protocol: packet_protocols = 0x0 (2 bytes)
// sll_ifindex: ifindex (resource)
// sll_hatype: const = 0x1 (2 bytes)
// sll_pkttype: int8 = 0x0 (1 bytes)
// sll_halen: const = 0x6 (1 bytes)
// sll_addr: union mac_addr {
// broadcast: buffer: (DirOut)
// }
// pad: buffer: (DirOut)
// }
// }
// addrlen: ptr[inout, len] {
// len = 0x14 (4 bytes)
// }
// ]
*(uint32_t*)0x200000001480 = 0x14;
res = syscall(__NR_getsockname, /*fd=*/r[0], /*addr=*/0x200000000200ul, /*addrlen=*/0x200000001480ul);
if (res != -1)
r[1] = *(uint32_t*)0x200000000204;
// sendmsg$nl_route_sched arguments: [
// fd: sock_nl_route (resource)
// msg: ptr[in, msghdr_netlink[netlink_msg_route_sched]] {
// msghdr_netlink[netlink_msg_route_sched] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, netlink_msg_route_sched]] {
// iovec[in, netlink_msg_route_sched] {
// addr: ptr[in, netlink_msg_route_sched] {
// union netlink_msg_route_sched {
// newqdisc: netlink_msg_t[const[RTM_NEWQDISC, int16], tcmsg[AF_UNSPEC], rtm_tca_policy] {
// len: len = 0x44 (4 bytes)
// type: const = 0x24 (2 bytes)
// flags: netlink_msg_flags = 0xf0b (2 bytes)
// seq: int32 = 0x0 (4 bytes)
// pid: int32 = 0x0 (4 bytes)
// payload: tcmsg[AF_UNSPEC] {
// family: const = 0x0 (1 bytes)
// tcm__pad1: const = 0x0 (1 bytes)
// tcm__pad2: const = 0x0 (2 bytes)
// ifindex: ifindex (resource)
// tcm_handle: tcm_handle {
// minor: tcm_handle_offsets = 0x0 (2 bytes)
// major: tcm_handle_offsets = 0x0 (2 bytes)
// }
// tcm_parent: tcm_handle {
// minor: tcm_handle_offsets = 0xffff (2 bytes)
// major: tcm_handle_offsets = 0xffff (2 bytes)
// }
// tcm_info: tcm_handle {
// minor: tcm_handle_offsets = 0x0 (2 bytes)
// major: tcm_handle_offsets = 0x0 (2 bytes)
// }
// }
// attrs: array[rtm_tca_policy] {
// union rtm_tca_policy {
// qdisc_kind_options: union qdisc_kind_options {
// q_hfsc: tca_kind_options_t["hfsc", hfsc_policy] {
// TCA_KIND: nlattr_t[const[TCA_KIND, int16], string["hfsc"]] {
// nla_len: offsetof = 0x9 (2 bytes)
// nla_type: const = 0x1 (2 bytes)
// payload: buffer: {68 66 73 63 00} (length 0x5)
// size: buffer: {} (length 0x0)
// pad = 0x0 (3 bytes)
// }
// TCA_OPTIONS: nlattr_t[const[TCA_OPTIONS, int16], hfsc_policy] {
// nla_len: offsetof = 0x14 (2 bytes)
// nla_type: const = 0x2 (2 bytes)
// payload: union hfsc_policy {
// TCA_HFSC_RSC: nlattr_t[const[TCA_HFSC_RSC, int16], tc_service_curve] {
// nla_len: offsetof = 0x10 (2 bytes)
// nla_type: const = 0x1 (2 bytes)
// payload: tc_service_curve {
// m1: int32 = 0x2 (4 bytes)
// d: int32 = 0x9 (4 bytes)
// m2: int32 = 0xf06 (4 bytes)
// }
// size: buffer: {} (length 0x0)
// }
// }
// size: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// }
// }
// }
// len: len = 0x44 (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4000840 (8 bytes)
// ]
*(uint64_t*)0x2000000002c0 = 0;
*(uint32_t*)0x2000000002c8 = 0;
*(uint64_t*)0x2000000002d0 = 0x200000000280;
*(uint64_t*)0x200000000280 = 0x200000000480;
*(uint32_t*)0x200000000480 = 0x44;
*(uint16_t*)0x200000000484 = 0x24;
*(uint16_t*)0x200000000486 = 0xf0b;
*(uint32_t*)0x200000000488 = 0;
*(uint32_t*)0x20000000048c = 0;
*(uint8_t*)0x200000000490 = 0;
*(uint8_t*)0x200000000491 = 0;
*(uint16_t*)0x200000000492 = 0;
*(uint32_t*)0x200000000494 = r[1];
*(uint16_t*)0x200000000498 = 0;
*(uint16_t*)0x20000000049a = 0;
*(uint16_t*)0x20000000049c = -1;
*(uint16_t*)0x20000000049e = -1;
*(uint16_t*)0x2000000004a0 = 0;
*(uint16_t*)0x2000000004a2 = 0;
*(uint16_t*)0x2000000004a4 = 9;
*(uint16_t*)0x2000000004a6 = 1;
memcpy((void*)0x2000000004a8, "hfsc\000", 5);
*(uint16_t*)0x2000000004b0 = 0x14;
*(uint16_t*)0x2000000004b2 = 2;
*(uint16_t*)0x2000000004b4 = 0x10;
*(uint16_t*)0x2000000004b6 = 1;
*(uint32_t*)0x2000000004b8 = 2;
*(uint32_t*)0x2000000004bc = 9;
*(uint32_t*)0x2000000004c0 = 0xf06;
*(uint64_t*)0x200000000288 = 0x44;
*(uint64_t*)0x2000000002d8 = 1;
*(uint64_t*)0x2000000002e0 = 0;
*(uint64_t*)0x2000000002e8 = 0;
*(uint32_t*)0x2000000002f0 = 0;
syscall(__NR_sendmsg, /*fd=*/(intptr_t)-1, /*msg=*/0x2000000002c0ul, /*f=MSG_ZEROCOPY|MSG_DONTWAIT|MSG_CONFIRM*/0x4000840ul);
// socket$netlink arguments: [
// domain: const = 0x10 (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: netlink_proto = 0x0 (4 bytes)
// ]
// returns sock_netlink
res = syscall(__NR_socket, /*domain=*/0x10ul, /*type=*/3ul, /*proto=*/0);
if (res != -1)
r[2] = res;
// sendmmsg arguments: [
// fd: sock (resource)
// mmsg: ptr[in, array[send_mmsghdr]] {
// array[send_mmsghdr] {
// }
// }
// vlen: len = 0x40000000000009f (8 bytes)
// f: send_flags = 0x0 (8 bytes)
// ]
syscall(__NR_sendmmsg, /*fd=*/r[2], /*mmsg=*/0x2000000002c0ul, /*vlen=*/0x40000000000009ful, /*f=*/0ul);
// socket$nl_generic arguments: [
// domain: const = 0x10 (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: const = 0x10 (4 bytes)
// ]
// returns sock_nl_generic
res = syscall(__NR_socket, /*domain=*/0x10ul, /*type=*/3ul, /*proto=*/0x10);
if (res != -1)
r[3] = res;
// sendmsg$nl_generic arguments: [
// fd: sock_nl_generic (resource)
// msg: ptr[in, msghdr_netlink[netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]]] {
// msghdr_netlink[netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]]] {
// iovec[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// addr: ptr[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr] {
// len: len = 0x1c (4 bytes)
// type: int16 = 0x26 (2 bytes)
// flags: netlink_msg_flags = 0x9 (2 bytes)
// seq: int32 = 0x70bd25 (4 bytes)
// pid: int32 = 0x25dfdbfa (4 bytes)
// payload: genlmsghdr {
// cmd: int8 = 0x2 (1 bytes)
// version: const = 0x0 (1 bytes)
// reserved: const = 0x0 (2 bytes)
// }
// attrs: array[nl_generic_attr] {
// union nl_generic_attr {
// typed: nlattr_tt[int16:14[0:NETLINK_MAX_ATTRIBUTES], 0, 0, nl_generic_attr_data] {
// nla_len: offsetof = 0x8 (2 bytes)
// nla_type: int16 = 0x1 (1 bytes)
// NLA_F_NET_BYTEORDER: const = 0x0 (0 bytes)
// NLA_F_NESTED: const = 0x0 (1 bytes)
// payload: union nl_generic_attr_data {
// u32: int32 = 0x3 (4 bytes)
// }
// size: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// len: len = 0x1c (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x4000002 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4000080 (8 bytes)
// ]
*(uint64_t*)0x200000000100 = 0;
*(uint32_t*)0x200000000108 = 0;
*(uint64_t*)0x200000000110 = 0x200000000180;
*(uint64_t*)0x200000000180 = 0x200000000300;
*(uint32_t*)0x200000000300 = 0x1c;
*(uint16_t*)0x200000000304 = 0x26;
*(uint16_t*)0x200000000306 = 9;
*(uint32_t*)0x200000000308 = 0x70bd25;
*(uint32_t*)0x20000000030c = 0x25dfdbfa;
*(uint8_t*)0x200000000310 = 2;
*(uint8_t*)0x200000000311 = 0;
*(uint16_t*)0x200000000312 = 0;
*(uint16_t*)0x200000000314 = 8;
STORE_BY_BITMASK(uint16_t, , 0x200000000316, 1, 0, 14);
STORE_BY_BITMASK(uint16_t, , 0x200000000317, 0, 6, 1);
STORE_BY_BITMASK(uint16_t, , 0x200000000317, 0, 7, 1);
*(uint32_t*)0x200000000318 = 3;
*(uint64_t*)0x200000000188 = 0x1c;
*(uint64_t*)0x200000000118 = 1;
*(uint64_t*)0x200000000120 = 0;
*(uint64_t*)0x200000000128 = 0;
*(uint32_t*)0x200000000130 = 0x4000002;
syscall(__NR_sendmsg, /*fd=*/r[3], /*msg=*/0x200000000100ul, /*f=MSG_ZEROCOPY|MSG_EOR*/0x4000080ul);
return 0;
}
]
|
| 2/1 |
2026/06/01 05:21 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/b6f47d89cbf791530e18514d4ad687e0a047956d]
|
| 3/1 |
2026/06/01 05:21 |
action |
kernel-builder |
30m
Results: map[KernelObj:/app/workdir/cache/build/1bd3be712563623c90a131882524d84ab70a3e01]
|
| 4/1 |
2026/06/01 05:51 |
action |
codesearch-prepare |
21m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/06/01 06:13 |
agent |
expert |
14m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false 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 or elevated privileges.
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.
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:
INFO: task syz.3.20:5999 blocked for more than 143 seconds.
Not tainted syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.3.20 state:D
stack:26328 pid:5999 tgid:5999 ppid:5777 task_flags:0x400140 flags:0x00080002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5388 [inline]
__schedule+0x1295/0x67a0 kernel/sched/core.c:7189
__schedule_loop kernel/sched/core.c:7268 [inline]
schedule+0xdd/0x390 kernel/sched/core.c:7283
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7340
__mutex_lock_common kernel/locking/mutex.c:726 [inline]
__mutex_lock+0xced/0x1b10 kernel/locking/mutex.c:820
nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x9e1/0xb70 net/socket.c:2698
___sys_sendmsg+0x190/0x1e0 net/socket.c:2752
__sys_sendmsg+0x170/0x220 net/socket.c:2784
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f00c3d9ce59
RSP: 002b:00007ffeb0fee798 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f00c4015fa0 RCX: 00007f00c3d9ce59
RDX: 0000000004000080 RSI: 0000200000000100 RDI: 0000000000000005
RBP: 00007f00c3e32d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f00c4015fac R14: 00007f00c4015fa0 R15: 00007f00c4015fa0
</TASK>
INFO: task syz.1.18:6009 blocked for more than 144 seconds.
Not tainted syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.1.18 state:D stack:26328 pid:6009 tgid:6009 ppid:5782 task_flags:0x400140 flags:0x00080002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5388 [inline]
__schedule+0x1295/0x67a0 kernel/sched/core.c:7189
__schedule_loop kernel/sched/core.c:7268 [inline]
schedule+0xdd/0x390 kernel/sched/core.c:7283
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7340
__mutex_lock_common kernel/locking/mutex.c:726 [inline]
__mutex_lock+0xced/0x1b10 kernel/locking/mutex.c:820
nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x9e1/0xb70 net/socket.c:2698
___sys_sendmsg+0x190/0x1e0 net/socket.c:2752
__sys_sendmsg+0x170/0x220 net/socket.c:2784
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f3ca379ce59
RSP: 002b:00007fff1c6f9268 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f3ca3a15fa0 RCX: 00007f3ca379ce59
RDX: 0000000004000080 RSI: 0000200000000100 RDI: 0000000000000005
RBP: 00007f3ca3832d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f3ca3a15fac R14: 00007f3ca3a15fa0 R15: 00007f3ca3a15fa0
</TASK>
INFO: task syz.0.17:6011 blocked for more than 144 seconds.
Not tainted syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.0.17 state:D stack:26328 pid:6011 tgid:6011 ppid:5772 task_flags:0x400140 flags:0x00080002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5388 [inline]
__schedule+0x1295/0x67a0 kernel/sched/core.c:7189
__schedule_loop kernel/sched/core.c:7268 [inline]
schedule+0xdd/0x390 kernel/sched/core.c:7283
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7340
__mutex_lock_common kernel/locking/mutex.c:726 [inline]
__mutex_lock+0xced/0x1b10 kernel/locking/mutex.c:820
nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x9e1/0xb70 net/socket.c:2698
___sys_sendmsg+0x190/0x1e0 net/socket.c:2752
__sys_sendmsg+0x170/0x220 net/socket.c:2784
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fc87df9ce59
RSP: 002b:00007ffd50394178 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007fc87e215fa0 RCX: 00007fc87df9ce59
RDX: 0000000004000080 RSI: 0000200000000100 RDI: 0000000000000005
RBP: 00007fc87e032d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fc87e215fac R14: 00007fc87e215fa0 R15: 00007fc87e215fa0
</TASK>
INFO: task syz.4.21:6012 blocked for more than 145 seconds.
Not tainted syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.4.21 state:D stack:26328 pid:6012 tgid:6012 ppid:5783 task_flags:0x400140 flags:0x00080002
Call Trace:
<TASK>
context_switch kernel/sched/core.c:5388 [inline]
__schedule+0x1295/0x67a0 kernel/sched/core.c:7189
__schedule_loop kernel/sched/core.c:7268 [inline]
schedule+0xdd/0x390 kernel/sched/core.c:7283
schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7340
__mutex_lock_common kernel/locking/mutex.c:726 [inline]
__mutex_lock+0xced/0x1b10 kernel/locking/mutex.c:820
nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
____sys_sendmsg+0x9e1/0xb70 net/socket.c:2698
___sys_sendmsg+0x190/0x1e0 net/socket.c:2752
__sys_sendmsg+0x170/0x220 net/socket.c:2784
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fafccb9ce59
RSP: 002b:00007ffdb3251b98 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007fafcce15fa0 RCX: 00007fafccb9ce59
RDX: 0000000004000080 RSI: 0000200000000100 RDI: 0000000000000005
RBP: 00007fafccc32d6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fafcce15fac R14: 00007fafcce15fa0 R15: 00007fafcce15fa0
</TASK>
Showing all locks held in the system:
1 lock held by khungtaskd/30:
#0: ffffffff8e7e57a0 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
#0: ffffffff8e7e57a0 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
#0: ffffffff8e7e57a0 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x3d/0x184 kernel/locking/lockdep.c:6775
3 locks held by kworker/u8:10/1174:
#0: ffff8880331a7940 ((wq_completion)ipv6_addrconf){+.+.}-{0:0}, at: process_one_work+0x12d6/0x1980 kernel/workqueue.c:3289
#1: ffffc900058bfd08 ((work_completion)(&(&ifa->dad_work)->work)){+.+.}-{0:0}, at: process_one_work+0x973/0x1980 kernel/workqueue.c:3290
#2: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_net_lock include/linux/rtnetlink.h:130 [inline]
#2: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: addrconf_dad_work+0x11f/0x1360 net/ipv6/addrconf.c:4206
2 locks held by getty/5368:
#0: ffff888033adc0a0 (&tty->ldisc_sem){++++}-{0:0}, at: tty_ldisc_ref_wait+0x24/0x80 drivers/tty/tty_ldisc.c:243
#1: ffffc9000322b2e8 (&ldata->atomic_read_lock){+.+.}-{4:4}, at: n_tty_read+0x419/0x14f0 drivers/tty/n_tty.c:2211
3 locks held by kworker/1:6/5964:
#0: ffff88813fe5b140 ((wq_completion)events){+.+.}-{0:0}, at: process_one_work+0x12d6/0x1980 kernel/workqueue.c:3289
#1: ffffc90002f57d08 ((work_completion)(&data->fib_event_work)){+.+.}-{0:0}, at: process_one_work+0x973/0x1980 kernel/workqueue.c:3290
#2: ffff888068b29250 (&data->fib_lock){+.+.}-{4:4}, at: nsim_fib_event_work+0x1b8/0x63b0 drivers/net/netdevsim/fib.c:1490
2 locks held by syz.2.19/5987:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.3.20/5999:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.1.18/6009:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.0.17/6011:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.4.21/6012:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.6.23/6215:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.5.22/6221:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.9.26/6224:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.8.25/6225:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.7.24/6226:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.2.27/6338:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.0.29/6391:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.3.28/6410:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.4.31/6411:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.1.30/6412:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.6.32/6457:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.7.33/6557:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.9.36/6597:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.5.35/6620:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.8.34/6621:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.2.37/6663:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.0.38/6703:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.3.39/6770:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.1.40/6809:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.4.41/6810:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.6.42/6852:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.7.43/6893:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.9.44/6957:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.8.45/7017:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.5.46/7018:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.2.47/7053:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.0.48/7090:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.3.49/7125:
#0: ffffffff906baec8
(cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.1.50/7195:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.4.51/7201:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.6.52/7242:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.7.53/7286:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
2 locks held by syz.9.54/7330:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
3 locks held by syz-executor/7333:
#0: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock net/core/rtnetlink.c:80 [inline]
#0: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_nets_lock net/core/rtnetlink.c:341 [inline]
#0: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_newlink+0x8bb/0x2380 net/core/rtnetlink.c:4109
#1: ffff88803b255590 (&wg->device_update_lock){+.+.}-{4:4}, at: wg_open+0x1fa/0x4d0 drivers/net/wireguard/device.c:50
#2: ffffffff8e7f12e8 (rcu_state.exp_mutex){+.+.}-{4:4}, at: exp_funnel_lock+0x27f/0x3c0 kernel/rcu/tree_exp.h:311
1 lock held by syz-executor/7334:
#0: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_net_lock include/linux/rtnetlink.h:130 [inline]
#0: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: inet_rtm_newaddr+0x30c/0x18b0 net/ipv4/devinet.c:978
2 locks held by syz-executor/7406:
#0: ffffffff905f3c88 (pernet_ops_rwsem){++++}-{4:4}, at: copy_net_ns+0x451/0x7c0 net/core/net_namespace.c:575
#1: ffffffff9060c9e0 (rtnl_mutex){+.+.}-{4:4}, at: ip_tunnel_init_net+0x21e/0x780 net/ipv4/ip_tunnel.c:1146
=============================================
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 30 Comm: khungtaskd Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
nmi_cpu_backtrace.cold+0x12d/0x151 lib/nmi_backtrace.c:113
nmi_trigger_cpumask_backtrace+0x1d7/0x230 lib/nmi_backtrace.c:62
trigger_all_cpu_backtrace include/linux/nmi.h:162 [inline]
__sys_info lib/sys_info.c:157 [inline]
sys_info+0x141/0x190 lib/sys_info.c:165
check_hung_uninterruptible_tasks kernel/hung_task.c:353 [inline]
watchdog+0xcb1/0x1030 kernel/hung_task.c:561
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Sending NMI from CPU 1 to CPUs 0:
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 7333 Comm: syz-executor Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
RIP: 0010:mark_held_locks+0x31/0x70 kernel/locking/lockdep.c:4322
Code: c0 7e 5a 41 55 49 89 fd 41 54 41 89 f4 55 31 ed 53 48 8d 9f 90 0b 00 00 0f b6 43 22 89 c2 83 e2 03 80 fa 01 44 89 e2 83 da ff <a8> 04 74 0f 48 89 de 4c 89 ef e8 90 f5 ff ff 85 c0 74 15 83 c5 01
RSP: 0018:ffffc90006f0ee20 EFLAGS: 00000013
RAX: 0000000000000005 RBX: ffff88807c6e3090 RCX: 0000000000000004
RDX: 0000000000000007 RSI: 0000000000000006 RDI: ffff88807c6e2500
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000006
R13: ffff88807c6e2500 R14: 0000000000000000 R15: 0000000000000000
FS: 00005555926b5500(0000) GS:ffff888124370000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007faca96e9e9c CR3: 0000000072ae8000 CR4: 00000000003526f0
Call Trace:
<TASK>
__trace_hardirqs_on_caller kernel/locking/lockdep.c:4351 [inline]
lockdep_hardirqs_on_prepare kernel/locking/lockdep.c:4410 [inline]
lockdep_hardirqs_on_prepare+0xf3/0x1b0 kernel/locking/lockdep.c:4362
trace_hardirqs_on+0x36/0x40 kernel/trace/trace_preemptirq.c:78
irqentry_exit_to_kernel_mode_after_preempt include/linux/irq-entry-common.h:515 [inline]
irqentry_exit_to_kernel_mode include/linux/irq-entry-common.h:550 [inline]
irqentry_exit+0x24d/0x7e0 kernel/entry/common.c:164
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:console_trylock_spinning kernel/printk/printk.c:2039 [inline]
RIP: 0010:vprintk_emit+0x553/0x6b0 kernel/printk/printk.c:2478
Code: 00 4d 85 ed 0f 85 1b 01 00 00 e8 08 5e 21 00 9c 5d 81 e5 00 02 00 00 31 ff 48 89 ee e8 d6 58 21 00 48 85 ed 0f 85 27 01 00 00 <e8> e8 5d 21 00 45 31 c9 41 b8 01 00 00 00 31 c9 48 8d 05 00 00 00
RSP: 0018:ffffc90006f0ef58 EFLAGS: 00000293
RAX: 0000000000000000 RBX: 000000000000003e RCX: ffffffff81e7420a
RDX: ffff88807c6e2500 RSI: ffffffff81e74214 RDI: ffff88807c6e2500
RBP: 0000000000000000 R08: 0000000000000007 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 1ffff92000de1ded
R13: 0000000000000200 R14: ffff88801f28a500 R15: ffffc90006f0f120
dev_vprintk_emit+0x394/0x3e0 drivers/base/core.c:4930
dev_printk_emit+0xd2/0x10d drivers/base/core.c:4941
__dev_printk+0xcb/0x100 drivers/base/core.c:4953
_dev_printk+0xe8/0x130 drivers/base/core.c:4970
ieee80211_init_rate_ctrl_alg.cold+0xa9/0xdc net/mac80211/rate.c:1026
ieee80211_register_hw+0x2c20/0x4570 net/mac80211/main.c:1559
mac80211_hwsim_new_radio+0x2a01/0x5aa0 drivers/net/wireless/virtual/mac80211_hwsim.c:6019
hwsim_new_radio_nl+0xc5f/0x1370 drivers/net/wireless/virtual/mac80211_hwsim.c:6726
genl_family_rcv_msg_doit+0x214/0x300 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x560/0x800 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x159/0x420 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x585/0x850 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x8b0/0xda0 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:787 [inline]
__sock_sendmsg net/socket.c:802 [inline]
__sys_sendto+0x468/0x4b0 net/socket.c:2265
__do_sys_sendto net/socket.c:2272 [inline]
__se_sys_sendto net/socket.c:2268 [inline]
__x64_sys_sendto+0xe0/0x1c0 net/socket.c:2268
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x10b/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fa28db5d68e
Code: 08 0f 85 a5 a8 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 80 00 00 00 00 48 83 ec 08
RSP: 002b:00007fff0280b198 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
RAX: ffffffffffffffda RBX: 00005555926b5500 RCX: 00007fa28db5d68e
RDX: 0000000000000024 RSI: 00007fa28e944670 RDI: 0000000000000003
RBP: 0000000000000001 R08: 00007fff0280b214 R09: 000000000000000c
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000003
R13: 0000000000000000 R14: 00007fa28e944670 R15: 0000000000000000
</TASK>
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#define BITMASK(bf_off,bf_len) (((1ull << (bf_len)) - 1) << (bf_off))
#define STORE_BY_BITMASK(type,htobe,addr,val,bf_off,bf_len) *(type*)(addr) = htobe((htobe(*(type*)(addr)) & ~BITMASK((bf_off), (bf_len))) | (((type)(val) << (bf_off)) & BITMASK((bf_off), (bf_len))))
uint64_t r[4] = {0xffffffffffffffff, 0x0, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// socket arguments: [
// domain: socket_domain = 0x2a (8 bytes)
// type: socket_type = 0x2 (8 bytes)
// proto: int32 = 0x0 (4 bytes)
// ]
// returns sock
res = syscall(__NR_socket, /*domain=AF_QIPCRTR*/0x2aul, /*type=SOCK_DGRAM*/2ul, /*proto=*/0);
if (res != -1)
r[0] = res;
// getsockname$packet arguments: [
// fd: sock_packet (resource)
// addr: ptr[out, sockaddr_ll] {
// sockaddr_ll {
// sll_family: const = 0x11 (2 bytes)
// sll_protocol: packet_protocols = 0x0 (2 bytes)
// sll_ifindex: ifindex (resource)
// sll_hatype: const = 0x1 (2 bytes)
// sll_pkttype: int8 = 0x0 (1 bytes)
// sll_halen: const = 0x6 (1 bytes)
// sll_addr: union mac_addr {
// broadcast: buffer: (DirOut)
// }
// pad: buffer: (DirOut)
// }
// }
// addrlen: ptr[inout, len] {
// len = 0x14 (4 bytes)
// }
// ]
*(uint32_t*)0x200000001480 = 0x14;
res = syscall(__NR_getsockname, /*fd=*/r[0], /*addr=*/0x200000000200ul, /*addrlen=*/0x200000001480ul);
if (res != -1)
r[1] = *(uint32_t*)0x200000000204;
// sendmsg$nl_route_sched arguments: [
// fd: sock_nl_route (resource)
// msg: ptr[in, msghdr_netlink[netlink_msg_route_sched]] {
// msghdr_netlink[netlink_msg_route_sched] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, netlink_msg_route_sched]] {
// iovec[in, netlink_msg_route_sched] {
// addr: ptr[in, netlink_msg_route_sched] {
// union netlink_msg_route_sched {
// newqdisc: netlink_msg_t[const[RTM_NEWQDISC, int16], tcmsg[AF_UNSPEC], rtm_tca_policy] {
// len: len = 0x44 (4 bytes)
// type: const = 0x24 (2 bytes)
// flags: netlink_msg_flags = 0xf0b (2 bytes)
// seq: int32 = 0x0 (4 bytes)
// pid: int32 = 0x0 (4 bytes)
// payload: tcmsg[AF_UNSPEC] {
// family: const = 0x0 (1 bytes)
// tcm__pad1: const = 0x0 (1 bytes)
// tcm__pad2: const = 0x0 (2 bytes)
// ifindex: ifindex (resource)
// tcm_handle: tcm_handle {
// minor: tcm_handle_offsets = 0x0 (2 bytes)
// major: tcm_handle_offsets = 0x0 (2 bytes)
// }
// tcm_parent: tcm_handle {
// minor: tcm_handle_offsets = 0xffff (2 bytes)
// major: tcm_handle_offsets = 0xffff (2 bytes)
// }
// tcm_info: tcm_handle {
// minor: tcm_handle_offsets = 0x0 (2 bytes)
// major: tcm_handle_offsets = 0x0 (2 bytes)
// }
// }
// attrs: array[rtm_tca_policy] {
// union rtm_tca_policy {
// qdisc_kind_options: union qdisc_kind_options {
// q_hfsc: tca_kind_options_t["hfsc", hfsc_policy] {
// TCA_KIND: nlattr_t[const[TCA_KIND, int16], string["hfsc"]] {
// nla_len: offsetof = 0x9 (2 bytes)
// nla_type: const = 0x1 (2 bytes)
// payload: buffer: {68 66 73 63 00} (length 0x5)
// size: buffer: {} (length 0x0)
// pad = 0x0 (3 bytes)
// }
// TCA_OPTIONS: nlattr_t[const[TCA_OPTIONS, int16], hfsc_policy] {
// nla_len: offsetof = 0x14 (2 bytes)
// nla_type: const = 0x2 (2 bytes)
// payload: union hfsc_policy {
// TCA_HFSC_RSC: nlattr_t[const[TCA_HFSC_RSC, int16], tc_service_curve] {
// nla_len: offsetof = 0x10 (2 bytes)
// nla_type: const = 0x1 (2 bytes)
// payload: tc_service_curve {
// m1: int32 = 0x2 (4 bytes)
// d: int32 = 0x9 (4 bytes)
// m2: int32 = 0xf06 (4 bytes)
// }
// size: buffer: {} (length 0x0)
// }
// }
// size: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// }
// }
// }
// len: len = 0x44 (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4000840 (8 bytes)
// ]
*(uint64_t*)0x2000000002c0 = 0;
*(uint32_t*)0x2000000002c8 = 0;
*(uint64_t*)0x2000000002d0 = 0x200000000280;
*(uint64_t*)0x200000000280 = 0x200000000480;
*(uint32_t*)0x200000000480 = 0x44;
*(uint16_t*)0x200000000484 = 0x24;
*(uint16_t*)0x200000000486 = 0xf0b;
*(uint32_t*)0x200000000488 = 0;
*(uint32_t*)0x20000000048c = 0;
*(uint8_t*)0x200000000490 = 0;
*(uint8_t*)0x200000000491 = 0;
*(uint16_t*)0x200000000492 = 0;
*(uint32_t*)0x200000000494 = r[1];
*(uint16_t*)0x200000000498 = 0;
*(uint16_t*)0x20000000049a = 0;
*(uint16_t*)0x20000000049c = -1;
*(uint16_t*)0x20000000049e = -1;
*(uint16_t*)0x2000000004a0 = 0;
*(uint16_t*)0x2000000004a2 = 0;
*(uint16_t*)0x2000000004a4 = 9;
*(uint16_t*)0x2000000004a6 = 1;
memcpy((void*)0x2000000004a8, "hfsc\000", 5);
*(uint16_t*)0x2000000004b0 = 0x14;
*(uint16_t*)0x2000000004b2 = 2;
*(uint16_t*)0x2000000004b4 = 0x10;
*(uint16_t*)0x2000000004b6 = 1;
*(uint32_t*)0x2000000004b8 = 2;
*(uint32_t*)0x2000000004bc = 9;
*(uint32_t*)0x2000000004c0 = 0xf06;
*(uint64_t*)0x200000000288 = 0x44;
*(uint64_t*)0x2000000002d8 = 1;
*(uint64_t*)0x2000000002e0 = 0;
*(uint64_t*)0x2000000002e8 = 0;
*(uint32_t*)0x2000000002f0 = 0;
syscall(__NR_sendmsg, /*fd=*/(intptr_t)-1, /*msg=*/0x2000000002c0ul, /*f=MSG_ZEROCOPY|MSG_DONTWAIT|MSG_CONFIRM*/0x4000840ul);
// socket$netlink arguments: [
// domain: const = 0x10 (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: netlink_proto = 0x0 (4 bytes)
// ]
// returns sock_netlink
res = syscall(__NR_socket, /*domain=*/0x10ul, /*type=*/3ul, /*proto=*/0);
if (res != -1)
r[2] = res;
// sendmmsg arguments: [
// fd: sock (resource)
// mmsg: ptr[in, array[send_mmsghdr]] {
// array[send_mmsghdr] {
// }
// }
// vlen: len = 0x40000000000009f (8 bytes)
// f: send_flags = 0x0 (8 bytes)
// ]
syscall(__NR_sendmmsg, /*fd=*/r[2], /*mmsg=*/0x2000000002c0ul, /*vlen=*/0x40000000000009ful, /*f=*/0ul);
// socket$nl_generic arguments: [
// domain: const = 0x10 (8 bytes)
// type: const = 0x3 (8 bytes)
// proto: const = 0x10 (4 bytes)
// ]
// returns sock_nl_generic
res = syscall(__NR_socket, /*domain=*/0x10ul, /*type=*/3ul, /*proto=*/0x10);
if (res != -1)
r[3] = res;
// sendmsg$nl_generic arguments: [
// fd: sock_nl_generic (resource)
// msg: ptr[in, msghdr_netlink[netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]]] {
// msghdr_netlink[netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// addr: nil
// addrlen: len = 0x0 (4 bytes)
// pad = 0x0 (4 bytes)
// vec: ptr[in, iovec[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]]] {
// iovec[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// addr: ptr[in, netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr]] {
// netlink_msg_t[netlink_random_msg_type, genlmsghdr, nl_generic_attr] {
// len: len = 0x1c (4 bytes)
// type: int16 = 0x26 (2 bytes)
// flags: netlink_msg_flags = 0x9 (2 bytes)
// seq: int32 = 0x70bd25 (4 bytes)
// pid: int32 = 0x25dfdbfa (4 bytes)
// payload: genlmsghdr {
// cmd: int8 = 0x2 (1 bytes)
// version: const = 0x0 (1 bytes)
// reserved: const = 0x0 (2 bytes)
// }
// attrs: array[nl_generic_attr] {
// union nl_generic_attr {
// typed: nlattr_tt[int16:14[0:NETLINK_MAX_ATTRIBUTES], 0, 0, nl_generic_attr_data] {
// nla_len: offsetof = 0x8 (2 bytes)
// nla_type: int16 = 0x1 (1 bytes)
// NLA_F_NET_BYTEORDER: const = 0x0 (0 bytes)
// NLA_F_NESTED: const = 0x0 (1 bytes)
// payload: union nl_generic_attr_data {
// u32: int32 = 0x3 (4 bytes)
// }
// size: buffer: {} (length 0x0)
// }
// }
// }
// }
// }
// len: len = 0x1c (8 bytes)
// }
// }
// vlen: const = 0x1 (8 bytes)
// ctrl: const = 0x0 (8 bytes)
// ctrllen: const = 0x0 (8 bytes)
// f: send_flags = 0x4000002 (4 bytes)
// pad = 0x0 (4 bytes)
// }
// }
// f: send_flags = 0x4000080 (8 bytes)
// ]
*(uint64_t*)0x200000000100 = 0;
*(uint32_t*)0x200000000108 = 0;
*(uint64_t*)0x200000000110 = 0x200000000180;
*(uint64_t*)0x200000000180 = 0x200000000300;
*(uint32_t*)0x200000000300 = 0x1c;
*(uint16_t*)0x200000000304 = 0x26;
*(uint16_t*)0x200000000306 = 9;
*(uint32_t*)0x200000000308 = 0x70bd25;
*(uint32_t*)0x20000000030c = 0x25dfdbfa;
*(uint8_t*)0x200000000310 = 2;
*(uint8_t*)0x200000000311 = 0;
*(uint16_t*)0x200000000312 = 0;
*(uint16_t*)0x200000000314 = 8;
STORE_BY_BITMASK(uint16_t, , 0x200000000316, 1, 0, 14);
STORE_BY_BITMASK(uint16_t, , 0x200000000317, 0, 6, 1);
STORE_BY_BITMASK(uint16_t, , 0x200000000317, 0, 7, 1);
*(uint32_t*)0x200000000318 = 3;
*(uint64_t*)0x200000000188 = 0x1c;
*(uint64_t*)0x200000000118 = 1;
*(uint64_t*)0x200000000120 = 0;
*(uint64_t*)0x200000000128 = 0;
*(uint32_t*)0x200000000130 = 0x4000002;
syscall(__NR_sendmsg, /*fd=*/r[3], /*msg=*/0x200000000100ul, /*f=MSG_ZEROCOPY|MSG_EOR*/0x4000080ul);
return 0;
}
Reply: Based on the analysis of the provided bug report and kernel source code, here is the assessment of the security impact:
### Bug Mechanism
The syzkaller report shows multiple tasks blocked for over 143 seconds in `__mutex_lock_common` while trying to acquire `nfsd_mutex` inside `nfsd_nl_threads_set_doit`.
The root cause of this hang is a deadlock/livelock involving `nfsd` thread shutdown and memory allocation failures.
When an `nfsd` thread processes a request, it allocates pages in `svc_alloc_arg()`. If `alloc_page(GFP_KERNEL)` fails (which is common under heavy load or OOM conditions generated by syzkaller), the thread sleeps for 500ms in `TASK_INTERRUPTIBLE` and retries indefinitely. It only breaks out of this loop if `signalled()` or `kthread_should_stop()` is true.
However, `nfsd` threads block most signals and use a custom stopping mechanism (`SP_NEED_VICTIM` flag checked via `svc_thread_should_stop(rqstp)`), meaning `kthread_should_stop()` will never return true for them. Thus, the thread loops forever.
When a user tries to reconfigure or shutdown the `nfsd` server (e.g., by sending a netlink message to set the number of threads to 0), `nfsd_nl_threads_set_doit()` acquires `nfsd_mutex` and eventually calls `svc_stop_kthreads()`. `svc_stop_kthreads()` sets the `SP_NEED_VICTIM` flag and waits for the `nfsd` threads to exit. Because the victim thread is stuck in the `svc_alloc_arg()` infinite loop and never checks `svc_thread_should_stop()`, it never exits.
Consequently, `svc_stop_kthreads()` hangs forever while holding `nfsd_mutex`. Any subsequent tasks attempting to configure `nfsd` will block on `nfsd_mutex` in `TASK_UNINTERRUPTIBLE` state, eventually triggering the hung task detector.
### Security Impact Assessment
* **Exploitable:** No.
This is a pure logic bug leading to a livelock/deadlock. It does not involve memory corruption, use-after-free, or out-of-bounds accesses that could be leveraged for privilege escalation or arbitrary code execution.
* **Denial Of Service:** Yes.
Triggering this bug causes the `nfsd` subsystem to hang permanently. Furthermore, tasks blocked in `TASK_UNINTERRUPTIBLE` will trigger the kernel's hung task panic (if `kernel.hung_task_panic` is enabled, which is common in many production environments), leading to a complete system crash.
* **Accessible From Unprivileged Processes:** No.
The netlink command `NFSD_CMD_THREADS_SET` requires `GENL_ADMIN_PERM`, which translates to the `CAP_NET_ADMIN` capability. A standard unprivileged process does not have this capability.
* **Accessible From User Namespaces:** Yes.
The `nfsd` netlink family is registered with `.netnsok = true`. An unprivileged user can create a new user namespace and a new network namespace, thereby gaining `CAP_NET_ADMIN` within that namespace. From there, they can start an `nfsd` instance, induce memory pressure, and send the netlink message to trigger the deadlock.
* **VM Guest / Host / Network / Peripheral / Filesystem Triggers:** No.
This bug is strictly related to the local configuration of the NFS server via netlink sockets and local memory pressure. It is not triggered by processing remote network traffic, mounting filesystems, or interacting with peripherals/VM boundaries.
|
| 6/2 |
2026/06/01 06:13 |
llm |
expert |
0m
Model:
Tokens:
input: 25777
output: 26
thoughts: 666
Thoughts: **Analyzing a NFSd Deadlock**
Okay, I've got a hung task report here. It looks like `syz.3.20` has been blocked for quite a while – over 143 seconds. The call trace points to it being stuck somewhere in the NFSd netlink thread handling, specifically `nfsd_nl_threads_set_doit`. Seems like it's waiting on `nfsd_mutex`.
Hmm, multiple tasks are blocked on that same mutex. That's a classic sign. Let's dig into who's actually *holding* that lock. Ah, the lockdep output is helpful. It shows `syz.2.19/5987` holding `cb_lock` and the `nfsd_mutex`. Wait, another one! `syz.3.20/5999` is *also* holding `cb_lock` and the `nfsd_mutex`!
But if they're holding the `nfsd_mutex`, why are they blocked? That's a little confusing. Then it hits me. Lockdep shows locks held or being acquired, and in some lockdep configurations, the mutex is added to the held lock list *before* actually sleeping. Or, it's blocked. So, let's go look at the implementation of `nfsd_nl_threads_set_doit` in `fs/nfsd/nfsctl.c`. That should clear things up.
|
| 7/2 |
2026/06/01 06:13 |
tool |
grepper |
0m
Args: map[Expression:nfsd_nl_threads_set_doit]
Results: map[Output:fs/nfsd/netlink.c=52=static const struct genl_split_ops nfsd_nl_ops[] = {
--
fs/nfsd/netlink.c-59- .cmd = NFSD_CMD_THREADS_SET,
fs/nfsd/netlink.c:60: .doit = nfsd_nl_threads_set_doit,
fs/nfsd/netlink.c-61- .policy = nfsd_threads_set_nl_policy,
--
fs/nfsd/netlink.h=19=int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
fs/nfsd/netlink.h-20- struct netlink_callback *cb);
fs/nfsd/netlink.h:21:int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info);
fs/nfsd/netlink.h-22-int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info);
--
fs/nfsd/nfsctl.c=1594=static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn)
--
fs/nfsd/nfsctl.c-1610-/**
fs/nfsd/nfsctl.c:1611: * nfsd_nl_threads_set_doit - set the number of running threads
fs/nfsd/nfsctl.c-1612- * @skb: reply buffer
--
fs/nfsd/nfsctl.c-1616- */
fs/nfsd/nfsctl.c:1617:int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
fs/nfsd/nfsctl.c-1618-{
]
|
| 8/2 |
2026/06/01 06:13 |
llm |
expert |
0m
Model:
Tokens:
input: 27313
output: 40
thoughts: 32
Thoughts: Okay, let's dive into this. I'm going to pull up `nfsd_nl_threads_set_doit` in `fs/nfsd/nfsctl.c`. I need to understand how the system's managing network file system daemon thread settings through this netlink interface. I'm especially interested in how this function impacts overall NFS performance and scalability. I'm expecting to see how this function sets the number of threads. Let's see how they handle error conditions and the validation of any input parameters, given this is user controlled, and what security measures are put into place. I need to understand the data structures involved, the locks used, and how concurrent access is handled. I want to pay attention to any potential bottlenecks or areas for optimization. The goal is to evaluate if this is designed for future scale.
|
| 9/2 |
2026/06/01 06:13 |
tool |
read-file |
0m
Args: map[File:fs/nfsd/nfsctl.c FirstLine:1617 LineCount:50]
Results: map[Contents:1617: int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
1618: {
1619: int *nthreads, nrpools = 0, i, ret = -EOPNOTSUPP, rem;
1620: struct net *net = genl_info_net(info);
1621: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1622: const struct nlattr *attr;
1623: const char *scope = NULL;
1624:
1625: if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_THREADS))
1626: return -EINVAL;
1627:
1628: /* count number of SERVER_THREADS values */
1629: nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
1630: GENL_HDRLEN, rem)
1631: nrpools++;
1632:
1633: mutex_lock(&nfsd_mutex);
1634:
1635: nthreads = kzalloc_objs(int, nrpools);
1636: if (!nthreads) {
1637: ret = -ENOMEM;
1638: goto out_unlock;
1639: }
1640:
1641: i = 0;
1642: nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
1643: GENL_HDRLEN, rem) {
1644: nthreads[i++] = nla_get_u32(attr);
1645: if (i >= nrpools)
1646: break;
1647: }
1648:
1649: if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
1650: info->attrs[NFSD_A_SERVER_LEASETIME] ||
1651: info->attrs[NFSD_A_SERVER_SCOPE] ||
1652: info->attrs[NFSD_A_SERVER_FH_KEY]) {
1653: ret = -EBUSY;
1654: if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
1655: goto out_unlock;
1656:
1657: ret = -EINVAL;
1658: attr = info->attrs[NFSD_A_SERVER_GRACETIME];
1659: if (attr) {
1660: u32 gracetime = nla_get_u32(attr);
1661:
1662: if (gracetime < 10 || gracetime > 3600)
1663: goto out_unlock;
1664:
1665: nn->nfsd4_grace = gracetime;
1666: }
]
|
| 10/2 |
2026/06/01 06:13 |
llm |
expert |
1m
Model:
Tokens:
input: 28399
output: 19
thoughts: 1261
Thoughts: **Analyzing a Potential NFSd Concurrency Issue**
Okay, so I'm looking at this code and something's not quite right. First, I noticed `nthreads` is being allocated – it looks like `kzalloc_objs` is being used. Wait, is that right? Let me double-check. No, it's not `kzalloc_objs`, it seems to be `kcalloc(nrpools, sizeof(int), GFP_KERNEL)`. Good, standard kernel function. But what's really happening here?
Ah, I see `nfsd_nl_threads_set_doit` is grabbing `mutex_lock(&nfsd_mutex)`. Okay, so `nfsd_mutex` is a critical section protecting something. If it's held, who's got it? Time to use the debugger and look for `nfsd_mutex` in the lockdep output.
The lockdep output points to a deadlock! It says `nfsd_nl_threads_set_doit` is holding `nfsd_mutex`, and several tasks are blocked on it. So, a number of tasks appear to be blocked, waiting to acquire `nfsd_mutex`. This is intriguing. Who *actually* has the mutex?
Okay, let's look at the lockdep output again. "Showing all locks held in the system..." Okay, so the system is telling me who holds what. And wait... none of those tasks are the ones holding `nfsd_mutex` except for the ones blocked on it? Huh.
Okay, let me think this through. If a task is blocked on a mutex, it means its trying to acquire it and then its sleeping in `__mutex_lock`, so lockdep would mark it as held? Hmm, but lockdep shows the held locks, so it shouldn't show it as held if it hasn't acquired it yet. Wait a second, the output lists locks held by *all* tasks. So, if the mutex is *held* by a task, it *should* be in the list, no matter if the task is blocked or running.
Okay, so the fact that so many tasks are supposedly holding `nfsd_mutex` makes me think something is up. Is it really a mutex? Maybe a semaphore? Wait, let's go back to the definition...
|
| 11/2 |
2026/06/01 06:14 |
tool |
grepper |
0m
Args: map[Expression:nfsd_mutex]
Results: map[Output:Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst=39=removal of listening sockets, and startup and shutdown of the server.
Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst:40:For nfsd this is done using nfsd_mutex.
--
fs/nfsd/export.c=255=static void expkey_flush(void)
--
fs/nfsd/export.c-257- /*
fs/nfsd/export.c:258: * Take the nfsd_mutex here to ensure that the file cache is not
fs/nfsd/export.c-259- * destroyed while we're in the middle of flushing.
fs/nfsd/export.c-260- */
fs/nfsd/export.c:261: mutex_lock(&nfsd_mutex);
fs/nfsd/export.c-262- nfsd_file_cache_purge(current->nsproxy->net_ns);
fs/nfsd/export.c:263: mutex_unlock(&nfsd_mutex);
fs/nfsd/export.c-264-}
--
fs/nfsd/filecache.c=776=nfsd_file_cache_init(void)
--
fs/nfsd/filecache.c-779-
fs/nfsd/filecache.c:780: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-781- if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
--
fs/nfsd/filecache.c=941=nfsd_file_cache_purge(struct net *net)
fs/nfsd/filecache.c-942-{
fs/nfsd/filecache.c:943: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-944- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
--
fs/nfsd/filecache.c=956=nfsd_file_cache_shutdown(void)
--
fs/nfsd/filecache.c-959-
fs/nfsd/filecache.c:960: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-961- if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
--
fs/nfsd/filecache.c=1384=int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
--
fs/nfsd/filecache.c-1391- /* Serialize with server shutdown */
fs/nfsd/filecache.c:1392: mutex_lock(&nfsd_mutex);
fs/nfsd/filecache.c-1393- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
--
fs/nfsd/filecache.c-1405- }
fs/nfsd/filecache.c:1406: mutex_unlock(&nfsd_mutex);
fs/nfsd/filecache.c-1407-
--
fs/nfsd/nfsctl.c=267=static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-299- error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
fs/nfsd/nfsctl.c:300: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-301- nn = net_generic(netns(file), nfsd_net_id);
--
fs/nfsd/nfsctl.c-305- error = -EINVAL;
fs/nfsd/nfsctl.c:306: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-307-
--
fs/nfsd/nfsctl.c=417=static ssize_t write_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-430- trace_nfsd_ctl_threads(net, newthreads);
fs/nfsd/nfsctl.c:431: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-432- rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
fs/nfsd/nfsctl.c:433: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-434- if (rv < 0)
--
fs/nfsd/nfsctl.c=464=static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-476-
fs/nfsd/nfsctl.c:477: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-478- npools = nfsd_nrpools(net);
--
fs/nfsd/nfsctl.c-484- */
fs/nfsd/nfsctl.c:485: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-486- strcpy(buf, "0\n");
--
fs/nfsd/nfsctl.c-534- kfree(nthreads);
fs/nfsd/nfsctl.c:535: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-536- return rv;
--
fs/nfsd/nfsctl.c=696=static ssize_t write_versions(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-699-
fs/nfsd/nfsctl.c:700: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-701- rv = __write_versions(file, buf, size);
fs/nfsd/nfsctl.c:702: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-703- return rv;
--
fs/nfsd/nfsctl.c=855=static ssize_t write_ports(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-858-
fs/nfsd/nfsctl.c:859: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-860- rv = __write_ports(file, buf, size, netns(file));
fs/nfsd/nfsctl.c:861: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-862- return rv;
--
fs/nfsd/nfsctl.c=889=static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-906- bsize &= ~(1024-1);
fs/nfsd/nfsctl.c:907: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-908- if (nn->nfsd_serv) {
fs/nfsd/nfsctl.c:909: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-910- return -EBUSY;
--
fs/nfsd/nfsctl.c-912- nfsd_max_blksize = bsize;
fs/nfsd/nfsctl.c:913: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-914- }
--
fs/nfsd/nfsctl.c=957=static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
--
fs/nfsd/nfsctl.c-961-
fs/nfsd/nfsctl.c:962: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-963- rv = __nfsd4_write_time(file, buf, size, time, nn);
fs/nfsd/nfsctl.c:964: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-965- return rv;
--
fs/nfsd/nfsctl.c=1062=static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-1066-
fs/nfsd/nfsctl.c:1067: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1068- rv = __write_recoverydir(file, buf, size, nn);
fs/nfsd/nfsctl.c:1069: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1070- return rv;
--
fs/nfsd/nfsctl.c=1488=int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
--
fs/nfsd/nfsctl.c-1493-
fs/nfsd/nfsctl.c:1494: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1495-
--
fs/nfsd/nfsctl.c-1578-out_unlock:
fs/nfsd/nfsctl.c:1579: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1580-
--
fs/nfsd/nfsctl.c-1588- *
fs/nfsd/nfsctl.c:1589: * Callers should hold nfsd_mutex, returns 0 on success or negative errno.
fs/nfsd/nfsctl.c-1590- * Callers must ensure the server is shut down (sv_nrthreads == 0),
--
fs/nfsd/nfsctl.c=1617=int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1632-
fs/nfsd/nfsctl.c:1633: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1634-
--
fs/nfsd/nfsctl.c-1698-out_unlock:
fs/nfsd/nfsctl.c:1699: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1700- kfree(nthreads);
--
fs/nfsd/nfsctl.c=1711=int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1727-
fs/nfsd/nfsctl.c:1728: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1729-
--
fs/nfsd/nfsctl.c-1757-
fs/nfsd/nfsctl.c:1758: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1759-
--
fs/nfsd/nfsctl.c-1764-err_unlock:
fs/nfsd/nfsctl.c:1765: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1766-err_free_msg:
--
fs/nfsd/nfsctl.c=1779=int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1787-
fs/nfsd/nfsctl.c:1788: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1789-
--
fs/nfsd/nfsctl.c-1791- if (nn->nfsd_serv) {
fs/nfsd/nfsctl.c:1792: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1793- return -EBUSY;
--
fs/nfsd/nfsctl.c-1834-
fs/nfsd/nfsctl.c:1835: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1836-
--
fs/nfsd/nfsctl.c=1847=int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1862-
fs/nfsd/nfsctl.c:1863: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1864- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfsctl.c-1906-
fs/nfsd/nfsctl.c:1907: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1908- genlmsg_end(skb, hdr);
--
fs/nfsd/nfsctl.c-1912-err_nfsd_unlock:
fs/nfsd/nfsctl.c:1913: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1914-err_free_msg:
--
fs/nfsd/nfsctl.c=1927=int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1937-
fs/nfsd/nfsctl.c:1938: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1939-
--
fs/nfsd/nfsctl.c-1941- if (err) {
fs/nfsd/nfsctl.c:1942: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1943- return err;
--
fs/nfsd/nfsctl.c-2057-out_unlock_mtx:
fs/nfsd/nfsctl.c:2058: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2059-
--
fs/nfsd/nfsctl.c=2070=int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-2087-
fs/nfsd/nfsctl.c:2088: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2089- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfsctl.c-2118-out_unlock_mtx:
fs/nfsd/nfsctl.c:2119: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2120- genlmsg_end(skb, hdr);
--
fs/nfsd/nfsctl.c-2125- spin_unlock_bh(&serv->sv_lock);
fs/nfsd/nfsctl.c:2126: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2127-err_free_msg:
--
fs/nfsd/nfsctl.c=2200=static __net_init int nfsd_net_init(struct net *net)
--
fs/nfsd/nfsctl.c-2230- nn->nfsd4_minorversions[i] = nfsd_support_version(4);
fs/nfsd/nfsctl.c:2231: nn->nfsd_info.mutex = &nfsd_mutex;
fs/nfsd/nfsctl.c-2232- nn->nfsd_serv = NULL;
--
fs/nfsd/nfsd.h=79=extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
fs/nfsd/nfsd.h:80:extern struct mutex nfsd_mutex;
fs/nfsd/nfsd.h-81-extern atomic_t nfsd_th_cnt; /* number of available threads */
--
fs/nfsd/nfssvc.c=55=static __be32 nfsd_init_request(struct svc_rqst *,
--
fs/nfsd/nfssvc.c-59-/*
fs/nfsd/nfssvc.c:60: * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
fs/nfsd/nfssvc.c-61- * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
fs/nfsd/nfssvc.c-62- *
fs/nfsd/nfssvc.c:63: * Finally, the nfsd_mutex also protects some of the global variables that are
fs/nfsd/nfssvc.c-64- * accessed when nfsd starts and that are settable via the write_* routines in
--
fs/nfsd/nfssvc.c-70- */
fs/nfsd/nfssvc.c:71:DEFINE_MUTEX(nfsd_mutex);
fs/nfsd/nfssvc.c-72-
--
fs/nfsd/nfssvc.c=240=int nfsd_nrthreads(struct net *net)
--
fs/nfsd/nfssvc.c-244-
fs/nfsd/nfssvc.c:245: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-246- if (nn->nfsd_serv)
--
fs/nfsd/nfssvc.c-248- rv += nn->nfsd_serv->sv_pools[i].sp_nrthrmax;
fs/nfsd/nfssvc.c:249: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-250- return rv;
--
fs/nfsd/nfssvc.c=490=static struct notifier_block nfsd_inet6addr_notifier = {
--
fs/nfsd/nfssvc.c-494-
fs/nfsd/nfssvc.c:495:/* Only used under nfsd_mutex, so this atomic may be overkill: */
fs/nfsd/nfssvc.c-496-static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
--
fs/nfsd/nfssvc.c=502=void nfsd_destroy_serv(struct net *net)
--
fs/nfsd/nfssvc.c-506-
fs/nfsd/nfssvc.c:507: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-508-
--
fs/nfsd/nfssvc.c=571=void nfsd_shutdown_threads(struct net *net)
--
fs/nfsd/nfssvc.c-575-
fs/nfsd/nfssvc.c:576: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-577- serv = nn->nfsd_serv;
fs/nfsd/nfssvc.c-578- if (serv == NULL) {
fs/nfsd/nfssvc.c:579: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-580- return;
--
fs/nfsd/nfssvc.c-585- nfsd_destroy_serv(net);
fs/nfsd/nfssvc.c:586: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-587-}
--
fs/nfsd/nfssvc.c=596=int nfsd_create_serv(struct net *net)
--
fs/nfsd/nfssvc.c-601-
fs/nfsd/nfssvc.c:602: WARN_ON(!mutex_is_locked(&nfsd_mutex));
fs/nfsd/nfssvc.c-603- if (nn->nfsd_serv)
--
fs/nfsd/nfssvc.c=680=int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
--
fs/nfsd/nfssvc.c-686-
fs/nfsd/nfssvc.c:687: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-688-
--
fs/nfsd/nfssvc.c=751=nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
--
fs/nfsd/nfssvc.c-756-
fs/nfsd/nfssvc.c:757: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-758-
--
fs/nfsd/nfssvc.c=883=nfsd(void *vrqstp)
--
fs/nfsd/nfssvc.c-915- /* No work arrived within the timeout window */
fs/nfsd/nfssvc.c:916: if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c-917- if (pool->sp_nrthreads > pool->sp_nrthrmin) {
--
fs/nfsd/nfssvc.c-921- } else {
fs/nfsd/nfssvc.c:922: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-923- }
--
fs/nfsd/nfssvc.c-930- if (pool->sp_nrthreads < pool->sp_nrthrmax) {
fs/nfsd/nfssvc.c:931: if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c-932- if (pool->sp_nrthreads < pool->sp_nrthrmax) {
--
fs/nfsd/nfssvc.c-940- }
fs/nfsd/nfssvc.c:941: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-942- } else {
--
fs/nfsd/nfssvc.c-958- if (have_mutex)
fs/nfsd/nfssvc.c:959: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-960- return 0;
]
|
| 12/2 |
2026/06/01 06:14 |
llm |
expert |
0m
Model:
Tokens:
input: 36630
output: 17
thoughts: 1053
Thoughts: **Analyzing the Mutex Issue**
Okay, so I'm looking at this `nfsd_mutex` problem. First thought: it's a global mutex. If multiple tasks are blocked on it, and it's not showing up as being held in the lockdep output, that seems odd. Maybe the task that holds it isn't listed, but the output explicitly says "Showing all locks held in the system:". I would suspect that the holding task is missing, but that claim is explicitly contradicted.
Could a task have grabbed the mutex and then exited without releasing it? No, that's not likely. The kernel would throw a "locks held by task exiting" warning. So, that's a dead end.
Okay, what if the task holding the mutex is *running* and not blocked? The hung tasks list wouldn't show it, but the lockdep output is supposed to show *all* held locks, not just those held by hung tasks, so that can't be it.
Let's re-examine the lockdep output:
```
1 lock held by khungtaskd/30:
3 locks held by kworker/u8:10/1174:
2 locks held by getty/5368:
3 locks held by kworker/1:6/5964:
2 locks held by syz.2.19/5987:
2 locks held by syz.3.20/5999:
2 locks held by syz.1.18/6009:
2 locks held by syz.0.17/6011:
2 locks held by syz.4.21/6012:
2 locks held by syz.6.23/6215:
2 locks held by syz.5.22/6221:
2 locks held by syz.9.26/6224:
2 locks held by syz.8.25/6225:
2 locks held by syz.7.24/6226:
2 locks held by syz.2.27/6338:
2 locks held by syz.0.29/6391:
2 locks held by syz.3.28/6410:
2 locks held by syz.4.31/6411:
2 locks held by syz.1.30/6412:
2 locks held by syz.6.32/6457:
2 locks held by syz.7.33/6557:
2 locks held by syz.9.36/6597:
2 locks held by syz.5.35/6620:
2 locks held by syz.8.34/6621:
2 locks held by syz.2.37/6663:
2 locks held by syz.0.38/6703:
2 locks held by syz.3.39/6770:
2 locks held by syz.1.40/6809:
2 locks held by syz.4.41/6810:
2 locks held by syz.6.42/6852:
2 locks held by syz.7.43/6893:
2 locks held by syz.9.44/6957:
2 locks held by syz.8.45/7017:
2 locks held by syz.5.46/7018:
2 locks held by syz.2.47/7053:
2 locks held by syz.0.48/7090:
2 locks held by syz.3.49/7125:
2 locks held by syz.1.50/7195:
2 locks held by syz.4.51/7201:
2 locks held by syz.6.52/7242:
2 locks held by syz.7.53/7286:
2 locks held by syz.9.54/7330:
3 locks held by syz-executor/7333:
1 lock held by syz-executor/7334:
2 locks held by syz-executor/7406:
```
Aha! Look at that
|
| 13/2 |
2026/06/01 06:15 |
tool |
grepper |
0m
Args:
Results: map[Output:
Full output is too long, showing 500 out of 1448 lines.
Use more precise expression if possible.
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c=139=static void qcom_adreno_smmu_set_stall(const void *cookie, bool enabled)
--
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c-162-
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c:163: spin_lock_irqsave(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c-164- reg = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_SCTLR);
--
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c-171- arm_smmu_cb_write(smmu, cfg->cbndx, ARM_SMMU_CB_SCTLR, reg);
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c:172: spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c-173-
--
drivers/iommu/arm/arm-smmu/arm-smmu.c=246=static void arm_smmu_tlb_sync_context(struct arm_smmu_domain *smmu_domain)
--
drivers/iommu/arm/arm-smmu/arm-smmu.c-250-
drivers/iommu/arm/arm-smmu/arm-smmu.c:251: spin_lock_irqsave(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu.c-252- __arm_smmu_tlb_sync(smmu, ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx),
drivers/iommu/arm/arm-smmu/arm-smmu.c-253- ARM_SMMU_CB_TLBSYNC, ARM_SMMU_CB_TLBSTATUS);
drivers/iommu/arm/arm-smmu/arm-smmu.c:254: spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu.c-255-}
--
drivers/iommu/arm/arm-smmu/arm-smmu.c=919=static struct iommu_domain *arm_smmu_domain_alloc_paging(struct device *dev)
--
drivers/iommu/arm/arm-smmu/arm-smmu.c-934- mutex_init(&smmu_domain->init_mutex);
drivers/iommu/arm/arm-smmu/arm-smmu.c:935: spin_lock_init(&smmu_domain->cb_lock);
drivers/iommu/arm/arm-smmu/arm-smmu.c-936- smmu_domain->domain.pgsize_bitmap = smmu->pgsize_bitmap;
--
drivers/iommu/arm/arm-smmu/arm-smmu.c=1335=static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
--
drivers/iommu/arm/arm-smmu/arm-smmu.c-1353-
drivers/iommu/arm/arm-smmu/arm-smmu.c:1354: spin_lock_irqsave(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu.c-1355- va = iova & ~0xfffUL;
--
drivers/iommu/arm/arm-smmu/arm-smmu.c-1363- 5, 50)) {
drivers/iommu/arm/arm-smmu/arm-smmu.c:1364: spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu.c-1365- dev_err(dev,
--
drivers/iommu/arm/arm-smmu/arm-smmu.c-1372- phys = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_PAR);
drivers/iommu/arm/arm-smmu/arm-smmu.c:1373: spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
drivers/iommu/arm/arm-smmu/arm-smmu.c-1374- if (phys & ARM_SMMU_CB_PAR_F) {
--
drivers/iommu/arm/arm-smmu/arm-smmu.h=378=struct arm_smmu_domain {
--
drivers/iommu/arm/arm-smmu/arm-smmu.h-385- struct mutex init_mutex; /* Protects smmu pointer */
drivers/iommu/arm/arm-smmu/arm-smmu.h:386: spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
drivers/iommu/arm/arm-smmu/arm-smmu.h-387- struct iommu_domain domain;
--
drivers/mfd/dln2.c=93=struct dln2_dev {
--
drivers/mfd/dln2.c-104- struct list_head event_cb_list;
drivers/mfd/dln2.c:105: spinlock_t event_cb_lock;
drivers/mfd/dln2.c-106-
--
drivers/mfd/dln2.c=120=int dln2_register_event_cb(struct platform_device *pdev, u16 id,
--
drivers/mfd/dln2.c-135-
drivers/mfd/dln2.c:136: spin_lock_irqsave(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-137-
--
drivers/mfd/dln2.c-147-
drivers/mfd/dln2.c:148: spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-149-
--
drivers/mfd/dln2.c=157=void dln2_unregister_event_cb(struct platform_device *pdev, u16 id)
--
drivers/mfd/dln2.c-163-
drivers/mfd/dln2.c:164: spin_lock_irqsave(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-165-
--
drivers/mfd/dln2.c-173-
drivers/mfd/dln2.c:174: spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-175-
--
drivers/mfd/dln2.c=234=static void dln2_rx(struct urb *urb)
--
drivers/mfd/dln2.c-286-
drivers/mfd/dln2.c:287: spin_lock_irqsave(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-288- dln2_run_event_callbacks(dln2, id, echo, data, len);
drivers/mfd/dln2.c:289: spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
drivers/mfd/dln2.c-290- } else {
--
drivers/mfd/dln2.c=760=static int dln2_probe(struct usb_interface *interface,
--
drivers/mfd/dln2.c-795-
drivers/mfd/dln2.c:796: spin_lock_init(&dln2->event_cb_lock);
drivers/mfd/dln2.c-797- spin_lock_init(&dln2->disconnect_lock);
--
drivers/misc/genwqe/card_base.h=180=static inline void genwqe_mapping_init(struct dma_mapping *m,
--
drivers/misc/genwqe/card_base.h-201- * @ddcb_waitqs: Associated wait queues (one per DDCB)
drivers/misc/genwqe/card_base.h:202: * @ddcb_lock: Lock to protect queuing operations
drivers/misc/genwqe/card_base.h-203- * @ddcb_waitq: Wait on next DDCB finishing
--
drivers/misc/genwqe/card_base.h=206=struct ddcb_queue {
--
drivers/misc/genwqe/card_base.h-221-
drivers/misc/genwqe/card_base.h:222: spinlock_t ddcb_lock; /* exclusive access to queue */
drivers/misc/genwqe/card_base.h-223- wait_queue_head_t busy_waitq; /* wait for ddcb processing */
--
drivers/misc/genwqe/card_ddcb.c=241=static int ddcb_requ_finished(struct genwqe_dev *cd, struct ddcb_requ *req)
--
drivers/misc/genwqe/card_ddcb.c-259- *
drivers/misc/genwqe/card_ddcb.c:260: * This function must only be called with ddcb_lock held.
drivers/misc/genwqe/card_ddcb.c-261- *
--
drivers/misc/genwqe/card_ddcb.c=365=static int genwqe_check_ddcb_queue(struct genwqe_dev *cd,
--
drivers/misc/genwqe/card_ddcb.c-371-
drivers/misc/genwqe/card_ddcb.c:372: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-373-
--
drivers/misc/genwqe/card_ddcb.c-455- go_home:
drivers/misc/genwqe/card_ddcb.c:456: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-457- return ddcbs_finished;
--
drivers/misc/genwqe/card_ddcb.c=477=int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req)
--
drivers/misc/genwqe/card_ddcb.c-562- * DDCB's content is completely cleared but presets for PRE and
drivers/misc/genwqe/card_ddcb.c:563: * SEQNUM. This function must only be called when ddcb_lock is held.
drivers/misc/genwqe/card_ddcb.c-564- *
--
drivers/misc/genwqe/card_ddcb.c=620=int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req)
--
drivers/misc/genwqe/card_ddcb.c-641-
drivers/misc/genwqe/card_ddcb.c:642: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-643-
--
drivers/misc/genwqe/card_ddcb.c-664-
drivers/misc/genwqe/card_ddcb.c:665: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-666-
--
drivers/misc/genwqe/card_ddcb.c-701-go_home:
drivers/misc/genwqe/card_ddcb.c:702: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-703- return 0;
--
drivers/misc/genwqe/card_ddcb.c=754=int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req,
--
drivers/misc/genwqe/card_ddcb.c-784- */
drivers/misc/genwqe/card_ddcb.c:785: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-786-
--
drivers/misc/genwqe/card_ddcb.c-790-
drivers/misc/genwqe/card_ddcb.c:791: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-792-
--
drivers/misc/genwqe/card_ddcb.c-809- if (queue->ddcb_req[req->num] != NULL) {
drivers/misc/genwqe/card_ddcb.c:810: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-811-
--
drivers/misc/genwqe/card_ddcb.c-898- ddcb_requ_set_state(req, GENWQE_REQU_TAPPED);
drivers/misc/genwqe/card_ddcb.c:899: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-900- wake_up_interruptible(&cd->queue_waitq);
--
drivers/misc/genwqe/card_ddcb.c=977=static int genwqe_next_ddcb_ready(struct genwqe_dev *cd)
--
drivers/misc/genwqe/card_ddcb.c-982-
drivers/misc/genwqe/card_ddcb.c:983: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-984-
drivers/misc/genwqe/card_ddcb.c-985- if (queue_empty(queue)) { /* empty queue */
drivers/misc/genwqe/card_ddcb.c:986: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-987- return 0;
--
drivers/misc/genwqe/card_ddcb.c-991- if (pddcb->icrc_hsi_shi_32 & DDCB_COMPLETED_BE32) { /* ddcb ready */
drivers/misc/genwqe/card_ddcb.c:992: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-993- return 1;
--
drivers/misc/genwqe/card_ddcb.c-995-
drivers/misc/genwqe/card_ddcb.c:996: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-997- return 0;
--
drivers/misc/genwqe/card_ddcb.c=1008=int genwqe_ddcbs_in_flight(struct genwqe_dev *cd)
--
drivers/misc/genwqe/card_ddcb.c-1013-
drivers/misc/genwqe/card_ddcb.c:1014: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-1015- ddcbs_in_flight += queue->ddcbs_in_flight;
drivers/misc/genwqe/card_ddcb.c:1016: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-1017-
--
drivers/misc/genwqe/card_ddcb.c=1021=static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
--
drivers/misc/genwqe/card_ddcb.c-1072-
drivers/misc/genwqe/card_ddcb.c:1073: spin_lock_init(&queue->ddcb_lock);
drivers/misc/genwqe/card_ddcb.c-1074- init_waitqueue_head(&queue->busy_waitq);
--
drivers/misc/genwqe/card_ddcb.c=1313=static int queue_wake_up_all(struct genwqe_dev *cd)
--
drivers/misc/genwqe/card_ddcb.c-1318-
drivers/misc/genwqe/card_ddcb.c:1319: spin_lock_irqsave(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-1320-
--
drivers/misc/genwqe/card_ddcb.c-1324- wake_up_interruptible(&queue->busy_waitq);
drivers/misc/genwqe/card_ddcb.c:1325: spin_unlock_irqrestore(&queue->ddcb_lock, flags);
drivers/misc/genwqe/card_ddcb.c-1326-
--
drivers/net/ethernet/intel/e100.c-35- * of Tx, Rx, and configuration commands is through the CSR.
drivers/net/ethernet/intel/e100.c:36: * cmd_lock serializes accesses to the CSR command register. cb_lock
drivers/net/ethernet/intel/e100.c-37- * protects the shared Command Block List (CBL).
--
drivers/net/ethernet/intel/e100.c=536=struct nic {
--
drivers/net/ethernet/intel/e100.c-548-
drivers/net/ethernet/intel/e100.c:549: spinlock_t cb_lock ____cacheline_aligned;
drivers/net/ethernet/intel/e100.c-550- spinlock_t cmd_lock;
--
drivers/net/ethernet/intel/e100.c=843=static int e100_exec_cb(struct nic *nic, struct sk_buff *skb,
--
drivers/net/ethernet/intel/e100.c-849-
drivers/net/ethernet/intel/e100.c:850: spin_lock_irqsave(&nic->cb_lock, flags);
drivers/net/ethernet/intel/e100.c-851-
--
drivers/net/ethernet/intel/e100.c-895-err_unlock:
drivers/net/ethernet/intel/e100.c:896: spin_unlock_irqrestore(&nic->cb_lock, flags);
drivers/net/ethernet/intel/e100.c-897-
--
drivers/net/ethernet/intel/e100.c=1804=static int e100_tx_clean(struct nic *nic)
--
drivers/net/ethernet/intel/e100.c-1809-
drivers/net/ethernet/intel/e100.c:1810: spin_lock(&nic->cb_lock);
drivers/net/ethernet/intel/e100.c-1811-
--
drivers/net/ethernet/intel/e100.c-1837-
drivers/net/ethernet/intel/e100.c:1838: spin_unlock(&nic->cb_lock);
drivers/net/ethernet/intel/e100.c-1839-
--
drivers/net/ethernet/intel/e100.c=2827=static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
--
drivers/net/ethernet/intel/e100.c-2897- /* locks must be initialized before calling hw_reset */
drivers/net/ethernet/intel/e100.c:2898: spin_lock_init(&nic->cb_lock);
drivers/net/ethernet/intel/e100.c-2899- spin_lock_init(&nic->cmd_lock);
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c=1316=static inline void cgx_link_change_handler(u64 lstat,
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1340- /* Ensure callback doesn't get unregistered until we finish it */
drivers/net/ethernet/marvell/octeontx2/af/cgx.c:1341: spin_lock(&lmac->event_cb_lock);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1342-
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1358-err:
drivers/net/ethernet/marvell/octeontx2/af/cgx.c:1359: spin_unlock(&lmac->event_cb_lock);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1360-}
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c=1452=int cgx_lmac_evh_unregister(void *cgxd, int lmac_id)
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1461-
drivers/net/ethernet/marvell/octeontx2/af/cgx.c:1462: spin_lock_irqsave(&lmac->event_cb_lock, flags);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1463- lmac->event_cb.notify_link_chg = NULL;
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1464- lmac->event_cb.data = NULL;
drivers/net/ethernet/marvell/octeontx2/af/cgx.c:1465: spin_unlock_irqrestore(&lmac->event_cb_lock, flags);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1466-
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c=1704=static int cgx_lmac_init(struct cgx *cgx)
--
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1769- mutex_init(&lmac->cmd_lock);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c:1770: spin_lock_init(&lmac->event_cb_lock);
drivers/net/ethernet/marvell/octeontx2/af/cgx.c-1771- err = cgx_configure_interrupt(cgx, lmac, lmac->lmac_id, false);
--
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h-22- * @event_cb: callback for linkchange events
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h:23: * @event_cb_lock: lock for serializing callback with unregister
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h-24- * @cgx: parent cgx port
--
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h=32=struct lmac {
--
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h-42- /* lock for serializing callback with unregister */
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h:43: spinlock_t event_cb_lock;
drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h-44- struct cgx *cgx;
--
drivers/rpmsg/virtio_rpmsg_bus.c=205=static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
--
drivers/rpmsg/virtio_rpmsg_bus.c-218- kref_init(&ept->refcount);
drivers/rpmsg/virtio_rpmsg_bus.c:219: mutex_init(&ept->cb_lock);
drivers/rpmsg/virtio_rpmsg_bus.c-220-
--
drivers/rpmsg/virtio_rpmsg_bus.c=294=__rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept)
--
drivers/rpmsg/virtio_rpmsg_bus.c-301- /* make sure in-flight inbound messages won't invoke cb anymore */
drivers/rpmsg/virtio_rpmsg_bus.c:302: mutex_lock(&ept->cb_lock);
drivers/rpmsg/virtio_rpmsg_bus.c-303- ept->cb = NULL;
drivers/rpmsg/virtio_rpmsg_bus.c:304: mutex_unlock(&ept->cb_lock);
drivers/rpmsg/virtio_rpmsg_bus.c-305-
--
drivers/rpmsg/virtio_rpmsg_bus.c=655=static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev,
--
drivers/rpmsg/virtio_rpmsg_bus.c-696- /* make sure ept->cb doesn't go away while we use it */
drivers/rpmsg/virtio_rpmsg_bus.c:697: mutex_lock(&ept->cb_lock);
drivers/rpmsg/virtio_rpmsg_bus.c-698-
--
drivers/rpmsg/virtio_rpmsg_bus.c-702-
drivers/rpmsg/virtio_rpmsg_bus.c:703: mutex_unlock(&ept->cb_lock);
drivers/rpmsg/virtio_rpmsg_bus.c-704-
--
drivers/usb/misc/usb-ljca.c=203=static void ljca_handle_event(struct ljca_adapter *adap,
--
drivers/usb/misc/usb-ljca.c-216-
drivers/usb/misc/usb-ljca.c:217: spin_lock_irqsave(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-218- client->event_cb(client->context, header->cmd,
drivers/usb/misc/usb-ljca.c-219- header->data, header->len);
drivers/usb/misc/usb-ljca.c:220: spin_unlock_irqrestore(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-221-
--
drivers/usb/misc/usb-ljca.c=396=int ljca_register_event_cb(struct ljca_client *client, ljca_event_cb_t event_cb,
--
drivers/usb/misc/usb-ljca.c-403-
drivers/usb/misc/usb-ljca.c:404: spin_lock_irqsave(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-405-
drivers/usb/misc/usb-ljca.c-406- if (client->event_cb) {
drivers/usb/misc/usb-ljca.c:407: spin_unlock_irqrestore(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-408- return -EALREADY;
--
drivers/usb/misc/usb-ljca.c-413-
drivers/usb/misc/usb-ljca.c:414: spin_unlock_irqrestore(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-415-
--
drivers/usb/misc/usb-ljca.c=420=void ljca_unregister_event_cb(struct ljca_client *client)
--
drivers/usb/misc/usb-ljca.c-423-
drivers/usb/misc/usb-ljca.c:424: spin_lock_irqsave(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-425-
--
drivers/usb/misc/usb-ljca.c-428-
drivers/usb/misc/usb-ljca.c:429: spin_unlock_irqrestore(&client->event_cb_lock, flags);
drivers/usb/misc/usb-ljca.c-430-}
--
drivers/usb/misc/usb-ljca.c=525=static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
--
drivers/usb/misc/usb-ljca.c-540- client->adapter = adap;
drivers/usb/misc/usb-ljca.c:541: spin_lock_init(&client->event_cb_lock);
drivers/usb/misc/usb-ljca.c-542-
--
fs/afs/callback.c=101=void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
fs/afs/callback.c-102-{
fs/afs/callback.c:103: write_seqlock(&vnode->cb_lock);
fs/afs/callback.c-104- __afs_break_callback(vnode, reason);
fs/afs/callback.c:105: write_sequnlock(&vnode->cb_lock);
fs/afs/callback.c-106-}
--
fs/afs/dir.c=1471=static void afs_dir_remove_link(struct afs_operation *op)
--
fs/afs/dir.c-1486- } else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
fs/afs/dir.c:1487: write_seqlock(&vnode->cb_lock);
fs/afs/dir.c-1488- drop_nlink(&vnode->netfs.inode);
--
fs/afs/dir.c-1492- }
fs/afs/dir.c:1493: write_sequnlock(&vnode->cb_lock);
fs/afs/dir.c-1494- } else {
--
fs/afs/dir.c=1823=static void afs_rename_success(struct afs_operation *op)
--
fs/afs/dir.c-1848-
fs/afs/dir.c:1849: write_seqlock(&vnode->cb_lock);
fs/afs/dir.c-1850-
--
fs/afs/dir.c-1855-
fs/afs/dir.c:1856: write_sequnlock(&vnode->cb_lock);
fs/afs/dir.c-1857- }
--
fs/afs/dir.c-1862-
fs/afs/dir.c:1863: write_seqlock(&new_vnode->cb_lock);
fs/afs/dir.c-1864-
--
fs/afs/dir.c-1868-
fs/afs/dir.c:1869: write_sequnlock(&new_vnode->cb_lock);
fs/afs/dir.c-1870- }
--
fs/afs/file.c=430=static void afs_update_i_size(struct inode *inode, loff_t new_i_size)
--
fs/afs/file.c-434-
fs/afs/file.c:435: write_seqlock(&vnode->cb_lock);
fs/afs/file.c-436- i_size = i_size_read(&vnode->netfs.inode);
--
fs/afs/file.c-440- }
fs/afs/file.c:441: write_sequnlock(&vnode->cb_lock);
fs/afs/file.c-442- fscache_update_cookie(afs_vnode_cache(vnode), NULL, &new_i_size);
--
fs/afs/file.c=477=static void afs_drop_open_mmap(struct afs_vnode *vnode)
--
fs/afs/file.c-483-
fs/afs/file.c:484: read_seqlock_excl(&vnode->cb_lock);
fs/afs/file.c-485- // the only place where ->cb_nr_mmap may hit 0
--
fs/afs/file.c-488- list_del_init(&vnode->cb_mmap_link);
fs/afs/file.c:489: read_sequnlock_excl(&vnode->cb_lock);
fs/afs/file.c-490-
--
fs/afs/inode.c=153=static int afs_inode_init_from_status(struct afs_operation *op,
--
fs/afs/inode.c-171-
fs/afs/inode.c:172: write_seqlock(&vnode->cb_lock);
fs/afs/inode.c-173-
--
fs/afs/inode.c-222- dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
fs/afs/inode.c:223: write_sequnlock(&vnode->cb_lock);
fs/afs/inode.c-224- return afs_protocol_error(NULL, afs_eproto_file_type);
--
fs/afs/inode.c-243-
fs/afs/inode.c:244: write_sequnlock(&vnode->cb_lock);
fs/afs/inode.c-245- return 0;
--
fs/afs/inode.c=379=void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *vp)
--
fs/afs/inode.c-384-
fs/afs/inode.c:385: write_seqlock(&vnode->cb_lock);
fs/afs/inode.c-386-
--
fs/afs/inode.c-417-out:
fs/afs/inode.c:418: write_sequnlock(&vnode->cb_lock);
fs/afs/inode.c-419-
--
fs/afs/inode.c=678=int afs_getattr(struct mnt_idmap *idmap, const struct path *path,
--
fs/afs/inode.c-700- do {
fs/afs/inode.c:701: seq = read_seqbegin(&vnode->cb_lock);
fs/afs/inode.c-702- generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
--
fs/afs/inode.c-712- stat->size = vnode->netfs.remote_i_size;
fs/afs/inode.c:713: } while (read_seqretry(&vnode->cb_lock, seq));
fs/afs/inode.c-714-
--
fs/afs/internal.h=686=struct afs_vnode {
--
fs/afs/internal.h-731- unsigned int cb_v_check; /* Break check counter on ->volume */
fs/afs/internal.h:732: seqlock_t cb_lock; /* Lock for ->cb_server, ->status, ->cb_*break */
fs/afs/internal.h-733-
--
fs/afs/rotate.c=34=static bool afs_start_fs_iteration(struct afs_operation *op,
--
fs/afs/rotate.c-98- /* Note that the callback promise is effectively broken */
fs/afs/rotate.c:99: write_seqlock(&vnode->cb_lock);
fs/afs/rotate.c-100- ASSERTCMP(cb_server, ==, vnode->cb_server);
--
fs/afs/rotate.c-103- vnode->cb_break++;
fs/afs/rotate.c:104: write_sequnlock(&vnode->cb_lock);
fs/afs/rotate.c-105- }
--
fs/afs/super.c=649=static void afs_i_init_once(void *_vnode)
--
fs/afs/super.c-663- INIT_LIST_HEAD(&vnode->cb_mmap_link);
fs/afs/super.c:664: seqlock_init(&vnode->cb_lock);
fs/afs/super.c-665-}
--
fs/aio.c=1725=static void aio_poll_put_work(struct work_struct *work)
--
fs/aio.c-1740- */
fs/aio.c:1741:static bool poll_iocb_lock_wq(struct poll_iocb *req)
fs/aio.c-1742-{
--
fs/aio.c=1778=static void aio_poll_complete_work(struct work_struct *work)
--
fs/aio.c-1796- spin_lock_irq(&ctx->ctx_lock);
fs/aio.c:1797: if (poll_iocb_lock_wq(req)) {
fs/aio.c-1798- if (!mask && !READ_ONCE(req->cancelled)) {
--
fs/aio.c=1824=static int aio_poll_cancel(struct kiocb *iocb)
--
fs/aio.c-1828-
fs/aio.c:1829: if (poll_iocb_lock_wq(req)) {
fs/aio.c-1830- WRITE_ONCE(req->cancelled, true);
--
fs/aio.c=1948=static int aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb)
--
fs/aio.c-1983- if (likely(apt.queued)) {
fs/aio.c:1984: bool on_queue = poll_iocb_lock_wq(req);
fs/aio.c-1985-
--
fs/dlm/ast.c=165=void dlm_add_cb(struct dlm_lkb *lkb, uint32_t flags, int mode, int status,
--
fs/dlm/ast.c-180-
fs/dlm/ast.c:181: spin_lock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-182- if (test_bit(LSFL_CB_DELAY, &ls->ls_flags)) {
--
fs/dlm/ast.c-198- }
fs/dlm/ast.c:199: spin_unlock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-200-}
--
fs/dlm/ast.c=223=void dlm_callback_suspend(struct dlm_ls *ls)
--
fs/dlm/ast.c-227-
fs/dlm/ast.c:228: spin_lock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-229- set_bit(LSFL_CB_DELAY, &ls->ls_flags);
fs/dlm/ast.c:230: spin_unlock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-231-
--
fs/dlm/ast.c=238=void dlm_callback_resume(struct dlm_ls *ls)
--
fs/dlm/ast.c-247-more:
fs/dlm/ast.c:248: spin_lock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-249- list_for_each_entry_safe(cb, safe, &ls->ls_cb_delay, list) {
--
fs/dlm/ast.c-262- clear_bit(LSFL_CB_DELAY, &ls->ls_flags);
fs/dlm/ast.c:263: spin_unlock_bh(&ls->ls_cb_lock);
fs/dlm/ast.c-264-
--
fs/dlm/dlm_internal.h=562=struct dlm_ls {
--
fs/dlm/dlm_internal.h-627-
fs/dlm/dlm_internal.h:628: spinlock_t ls_cb_lock;
fs/dlm/dlm_internal.h-629- struct list_head ls_cb_delay; /* save for queue_work later */
--
fs/dlm/lockspace.c=361=static int new_lockspace(const char *name, const char *cluster,
--
fs/dlm/lockspace.c-487-
fs/dlm/lockspace.c:488: spin_lock_init(&ls->ls_cb_lock);
fs/dlm/lockspace.c-489- INIT_LIST_HEAD(&ls->ls_cb_delay);
--
]
|
| 14/2 |
2026/06/01 06:15 |
llm |
expert |
0m
Model:
Tokens:
input: 49778
output: 19
thoughts: 45
Thoughts: Here's my take on those thoughts:
**Dissecting the Genetlink Lock**
Okay, so I'm looking at `cb_lock` within `net/netlink/genetlink.c`. That's a key area to understand when dealing with the Genl protocol in the kernel. My next thought is, let's zoom in on `genl_rcv`, also in `net/netlink/genetlink.c`. I need to figure out how this lock is being used in the receive path. I'm trying to pinpoint where contention might arise and how it impacts packet processing. This could be about concurrency issues, or perhaps investigating synchronization techniques.
|
| 15/2 |
2026/06/01 06:15 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/block/drbd/drbd_nl.c=195=static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
--
drivers/block/drbd/drbd_nl.c-201-
drivers/block/drbd/drbd_nl.c:202: /* genl_rcv_msg only checks for CAP_NET_ADMIN on "GENL_ADMIN_PERM" :( */
drivers/block/drbd/drbd_nl.c-203- if (cmd != DRBD_ADM_GET_STATUS && !capable(CAP_NET_ADMIN))
--
net/netlink/genetlink.c=1155=static int genl_family_rcv_msg(const struct genl_family *family,
--
net/netlink/genetlink.c-1197-
net/netlink/genetlink.c:1198:static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
net/netlink/genetlink.c-1199- struct netlink_ext_ack *extack)
--
net/netlink/genetlink.c-1214-
net/netlink/genetlink.c:1215:static void genl_rcv(struct sk_buff *skb)
net/netlink/genetlink.c-1216-{
net/netlink/genetlink.c-1217- down_read(&cb_lock);
net/netlink/genetlink.c:1218: netlink_rcv_skb(skb, &genl_rcv_msg);
net/netlink/genetlink.c-1219- up_read(&cb_lock);
--
net/netlink/genetlink.c=1877=static int __net_init genl_pernet_init(struct net *net)
--
net/netlink/genetlink.c-1879- struct netlink_kernel_cfg cfg = {
net/netlink/genetlink.c:1880: .input = genl_rcv,
net/netlink/genetlink.c-1881- .flags = NL_CFG_F_NONROOT_RECV,
--
net/nfc/netlink.c=1834=static void nfc_urelease_event_work(struct work_struct *work)
--
net/nfc/netlink.c-1866-
net/nfc/netlink.c:1867:static int nfc_genl_rcv_nl_event(struct notifier_block *this,
net/nfc/netlink.c-1868- unsigned long event, void *ptr)
--
net/nfc/netlink.c=1900=static struct notifier_block nl_notifier = {
net/nfc/netlink.c:1901: .notifier_call = nfc_genl_rcv_nl_event,
net/nfc/netlink.c-1902-};
]
|
| 16/2 |
2026/06/01 06:15 |
llm |
expert |
1m
Model:
Tokens:
input: 50771
output: 17
thoughts: 691
Thoughts: **Analyzing a Potential NFSd Mutex Contention**
Okay, so I'm looking at this trace and trying to piece together what's going on with this `nfsd_mutex`. The first thing that jumps out is that `cb_lock` is an `rwsem`, meaning multiple tasks can hold it for reading. Then, each reader calls into `netlink_rcv_skb`, which cascades down through `genl_rcv_msg`, `genl_family_rcv_msg`, `genl_family_rcv_msg_doit` and ends up in `nfsd_nl_threads_set_doit`. And it's in *that* function where the problem seems to be.
Inside `nfsd_nl_threads_set_doit`, the code calls `mutex_lock(&nfsd_mutex)`. Since all these tasks are doing this concurrently, they're all vying for the same mutex and queuing up. But what's really got me is how *long* this `nfsd_mutex` is being held up. This smells like a potential bottleneck.
So, I need to understand who's holding the mutex. I'm looking at the lockdep output to figure out. But it's not immediately clear. If a task isn't in the lockdep output holding the mutex, it can mean a few things: either that task is holding the mutex and is currently running, or that task is holding the mutex but is blocked somewhere else. I can eliminate the blocked case, because the lockdep output *should* include it. And, if it is running, it should be in the lockdep output.
Okay, let's look again at the lockdep output. I see that `syz.2.19/5987` is the culprit. It seems to be holding `nfsd_mutex`, but why is it blocked? Wait, if `syz.2.19/5987` is blocked *on* `nfsd_mutex`, it actually means it is *trying* to acquire it, but is blocked, so it is in `__mutex_lock`. Let me check the lockdep documentation again. Aha! The documentation says that lockdep adds the lock to the "held locks" list in `lock_acquire()`, which is called *before* `__mutex_lock`. So the lock is being shown as held, even though the task is blocked *waiting* to acquire it. Okay, that makes sense. Let me go back and double-check how `mutex_lock` is implemented.
|
| 17/2 |
2026/06/01 06:16 |
tool |
grepper |
0m
Args: map[Expression:mutex_lock]
Results: map[Output:
Full output is too long, showing 500 out of 141740 lines.
Use more precise expression if possible.
Documentation/RCU/rcubarrier.rst=194=The original code for rcu_barrier() was roughly as follows::
--
Documentation/RCU/rcubarrier.rst-199- 4 /* Take cpucontrol mutex to protect against CPU hotplug */
Documentation/RCU/rcubarrier.rst:200: 5 mutex_lock(&rcu_barrier_mutex);
Documentation/RCU/rcubarrier.rst-201- 6 init_completion(&rcu_barrier_completion);
--
Documentation/admin-guide/pstore-blk.rst=124=related hangs. Here is an example of usage::
--
Documentation/admin-guide/pstore-blk.rst-137- CPU:1 ts:5916720 c0160f59 c015ee04 kernfs_unmap_bin_file <- __kernfs_remove+0x140/0x204
Documentation/admin-guide/pstore-blk.rst:138: CPU:1 ts:5916721 c05ca625 c015ee0c __mutex_lock_slowpath <- __kernfs_remove+0x148/0x204
Documentation/admin-guide/pstore-blk.rst:139: CPU:1 ts:5916723 c05c813d c05ca630 yield_to <- __mutex_lock_slowpath+0x314/0x358
Documentation/admin-guide/pstore-blk.rst:140: CPU:1 ts:5916724 c05ca2d1 c05ca638 __ww_mutex_lock <- __mutex_lock_slowpath+0x31c/0x358
Documentation/admin-guide/pstore-blk.rst-141-
--
Documentation/core-api/kref.rst=174=For instance::
--
Documentation/core-api/kref.rst-186- struct my_data *entry = NULL;
Documentation/core-api/kref.rst:187: mutex_lock(&mutex);
Documentation/core-api/kref.rst-188- if (!list_empty(&q)) {
--
Documentation/core-api/kref.rst-205- {
Documentation/core-api/kref.rst:206: mutex_lock(&mutex);
Documentation/core-api/kref.rst-207- kref_put(&entry->refcount, release_entry);
--
Documentation/core-api/kref.rst=214=pointless to do so). You could use kref_put() as follows::
--
Documentation/core-api/kref.rst-222- {
Documentation/core-api/kref.rst:223: mutex_lock(&mutex);
Documentation/core-api/kref.rst-224- if (kref_put(&entry->refcount, release_entry)) {
--
Documentation/core-api/kref.rst=238=the following way::
--
Documentation/core-api/kref.rst-242- struct my_data *entry = NULL;
Documentation/core-api/kref.rst:243: mutex_lock(&mutex);
Documentation/core-api/kref.rst-244- if (!list_empty(&q)) {
--
Documentation/core-api/kref.rst-256-
Documentation/core-api/kref.rst:257: mutex_lock(&mutex);
Documentation/core-api/kref.rst-258- list_del(&entry->link);
--
Documentation/core-api/kref.rst=280=locking for lookups in the above example::
--
Documentation/core-api/kref.rst-307-
Documentation/core-api/kref.rst:308: mutex_lock(&mutex);
Documentation/core-api/kref.rst-309- list_del_rcu(&entry->link);
--
Documentation/core-api/refcount-vs-atomic.rst=185=Function changes:
--
Documentation/core-api/refcount-vs-atomic.rst-187- * atomic_dec_and_lock() --> refcount_dec_and_lock()
Documentation/core-api/refcount-vs-atomic.rst:188: * atomic_dec_and_mutex_lock() --> refcount_dec_and_mutex_lock()
Documentation/core-api/refcount-vs-atomic.rst-189-
--
Documentation/driver-api/media/v4l2-controls.rst=409=You can also take the handler lock yourself:
--
Documentation/driver-api/media/v4l2-controls.rst-412-
Documentation/driver-api/media/v4l2-controls.rst:413: mutex_lock(&state->ctrl_handler.lock);
Documentation/driver-api/media/v4l2-controls.rst-414- pr_info("String value is '%s'\n", ctrl1->p_cur.p_char);
--
Documentation/driver-api/media/v4l2-dev.rst=40=You should also set these fields of :c:type:`video_device`:
--
Documentation/driver-api/media/v4l2-dev.rst-65- locking in the driver. Otherwise you give it a pointer to a struct
Documentation/driver-api/media/v4l2-dev.rst:66: ``mutex_lock`` and before the :c:type:`video_device`->unlocked_ioctl
Documentation/driver-api/media/v4l2-dev.rst-67- file operation is called this lock will be taken by the core and released
--
Documentation/gpu/todo.rst=211=Many drivers have their own per-object locking scheme, usually using
Documentation/gpu/todo.rst:212:mutex_lock(). This causes all kinds of trouble for buffer sharing, since
Documentation/gpu/todo.rst-213-depending which driver is the exporter and importer, the locking hierarchy is
--
Documentation/gpu/todo.rst=563=This is a really varied tasks with lots of little bits and pieces:
--
Documentation/gpu/todo.rst-585-* The panic handler must never sleep, which also means it can't ever
Documentation/gpu/todo.rst:586: ``mutex_lock()``. Also it can't grab any other lock unconditionally, not
Documentation/gpu/todo.rst-587- even spinlocks (because NMI and hardirq can panic too). We need to either
--
Documentation/kernel-hacking/locking.rst=146=protect it. This is the most trivial case: you initialize the mutex.
Documentation/kernel-hacking/locking.rst:147:Then you can call mutex_lock_interruptible() to grab the
Documentation/kernel-hacking/locking.rst-148-mutex, and mutex_unlock() to release it. There is also a
Documentation/kernel-hacking/locking.rst:149:mutex_lock(), which should be avoided, because it will
Documentation/kernel-hacking/locking.rst-150-not return if a signal is received.
--
Documentation/kernel-hacking/locking.rst=335=Table: Table of Locking Requirements
--
Documentation/kernel-hacking/locking.rst-345-+--------+----------------------------+
Documentation/kernel-hacking/locking.rst:346:| MLI | mutex_lock_interruptible |
Documentation/kernel-hacking/locking.rst-347-+--------+----------------------------+
--
Documentation/kernel-hacking/locking.rst=383=to protect the cache and all the objects within it. Here's the code::
--
Documentation/kernel-hacking/locking.rst-451-
Documentation/kernel-hacking/locking.rst:452: mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-453- __cache_add(obj);
--
Documentation/kernel-hacking/locking.rst-459- {
Documentation/kernel-hacking/locking.rst:460: mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-461- __cache_delete(__cache_find(id));
--
Documentation/kernel-hacking/locking.rst-469-
Documentation/kernel-hacking/locking.rst:470: mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-471- obj = __cache_find(id);
--
Documentation/kernel-hacking/locking.rst=499=which are taken away, and the ``+`` are lines which are added.
--
Documentation/kernel-hacking/locking.rst-525-
Documentation/kernel-hacking/locking.rst:526: - mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-527- + spin_lock_irqsave(&cache_lock, flags);
--
Documentation/kernel-hacking/locking.rst-535- {
Documentation/kernel-hacking/locking.rst:536: - mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-537- + unsigned long flags;
--
Documentation/kernel-hacking/locking.rst-550-
Documentation/kernel-hacking/locking.rst:551: - mutex_lock(&cache_lock);
Documentation/kernel-hacking/locking.rst-552- + spin_lock_irqsave(&cache_lock, flags);
--
Documentation/kernel-hacking/locking.rst=1278=irq handler doesn't use a lock, and all other accesses are done as so::
Documentation/kernel-hacking/locking.rst-1279-
Documentation/kernel-hacking/locking.rst:1280: mutex_lock(&lock);
Documentation/kernel-hacking/locking.rst-1281- disable_irq(irq);
--
Documentation/kernel-hacking/locking.rst=1308=from user context, and can sleep.
--
Documentation/kernel-hacking/locking.rst-1321-
Documentation/kernel-hacking/locking.rst:1322:- mutex_lock_interruptible() and
Documentation/kernel-hacking/locking.rst:1323: mutex_lock()
Documentation/kernel-hacking/locking.rst-1324-
--
Documentation/locking/lockdep-design.rst=63=A contrived example::
--
Documentation/locking/lockdep-design.rst-65- modprobe/2287 is trying to acquire lock:
Documentation/locking/lockdep-design.rst:66: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/locking/lockdep-design.rst-67-
Documentation/locking/lockdep-design.rst-68- but task is already holding lock:
Documentation/locking/lockdep-design.rst:69: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/locking/lockdep-design.rst-70-
--
Documentation/locking/lockdep-design.rst=84=The bits are illustrated with an example::
Documentation/locking/lockdep-design.rst-85-
Documentation/locking/lockdep-design.rst:86: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/locking/lockdep-design.rst-87- ||||
--
Documentation/locking/lockdep-design.rst=213=looks like this::
Documentation/locking/lockdep-design.rst-214-
Documentation/locking/lockdep-design.rst:215: enum bdev_bd_mutex_lock_class
Documentation/locking/lockdep-design.rst-216- {
--
Documentation/locking/lockdep-design.rst-221-
Documentation/locking/lockdep-design.rst:222: mutex_lock_nested(&bdev->bd_contains->bd_mutex, BD_MUTEX_PARTITION);
Documentation/locking/lockdep-design.rst-223-
--
Documentation/locking/locktorture.rst=44=torture_type
--
Documentation/locking/locktorture.rst-64-
Documentation/locking/locktorture.rst:65: - "mutex_lock":
Documentation/locking/locktorture.rst:66: mutex_lock() and mutex_unlock() pairs.
Documentation/locking/locktorture.rst-67-
Documentation/locking/locktorture.rst:68: - "rtmutex_lock":
Documentation/locking/locktorture.rst:69: rtmutex_lock() and rtmutex_unlock() pairs.
Documentation/locking/locktorture.rst-70- Kernel must have CONFIG_RT_MUTEXES=y.
--
Documentation/locking/mutex-design.rst=133=Acquire the mutex, uninterruptible::
Documentation/locking/mutex-design.rst-134-
Documentation/locking/mutex-design.rst:135: void mutex_lock(struct mutex *lock);
Documentation/locking/mutex-design.rst:136: void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
Documentation/locking/mutex-design.rst-137- int mutex_trylock(struct mutex *lock);
--
Documentation/locking/mutex-design.rst=139=Acquire the mutex, interruptible::
Documentation/locking/mutex-design.rst-140-
Documentation/locking/mutex-design.rst:141: int mutex_lock_interruptible_nested(struct mutex *lock,
Documentation/locking/mutex-design.rst-142- unsigned int subclass);
Documentation/locking/mutex-design.rst:143: int mutex_lock_interruptible(struct mutex *lock);
Documentation/locking/mutex-design.rst-144-
Documentation/locking/mutex-design.rst=145=Acquire the mutex, interruptible, if dec to 0::
Documentation/locking/mutex-design.rst-146-
Documentation/locking/mutex-design.rst:147: int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
Documentation/locking/mutex-design.rst-148-
--
Documentation/locking/robust-futexes.rst=37=To solve such types of problems, "robust mutex" userspace APIs were
Documentation/locking/robust-futexes.rst:38:created: pthread_mutex_lock() returns an error value if the owner exits
Documentation/locking/robust-futexes.rst-39-prematurely - and the new owner can decide whether the data protected by
--
Documentation/locking/rt-mutex-design.rst=232=be directly nested that way::
--
Documentation/locking/rt-mutex-design.rst-235- {
Documentation/locking/rt-mutex-design.rst:236: mutex_lock(L1);
Documentation/locking/rt-mutex-design.rst-237-
--
Documentation/locking/rt-mutex-design.rst-244- {
Documentation/locking/rt-mutex-design.rst:245: mutex_lock(L1);
Documentation/locking/rt-mutex-design.rst:246: mutex_lock(L2);
Documentation/locking/rt-mutex-design.rst-247-
--
Documentation/locking/rt-mutex-design.rst-255- {
Documentation/locking/rt-mutex-design.rst:256: mutex_lock(L2);
Documentation/locking/rt-mutex-design.rst:257: mutex_lock(L3);
Documentation/locking/rt-mutex-design.rst-258-
--
Documentation/locking/rt-mutex-design.rst-266- {
Documentation/locking/rt-mutex-design.rst:267: mutex_lock(L3);
Documentation/locking/rt-mutex-design.rst-268-
--
Documentation/locking/ww-mutex-design.rst=74=Furthermore there are three different class of w/w lock acquire functions:
Documentation/locking/ww-mutex-design.rst-75-
Documentation/locking/ww-mutex-design.rst:76:* Normal lock acquisition with a context, using ww_mutex_lock.
Documentation/locking/ww-mutex-design.rst-77-
--
Documentation/locking/ww-mutex-design.rst-82- From a simple semantics point-of-view the _slow functions are not strictly
Documentation/locking/ww-mutex-design.rst:83: required, since simply calling the normal ww_mutex_lock functions on the
Documentation/locking/ww-mutex-design.rst-84- contending lock (after having dropped all other already acquired locks) will
Documentation/locking/ww-mutex-design.rst-85- work correctly. After all if no other ww mutex has been acquired yet there's
Documentation/locking/ww-mutex-design.rst:86: no deadlock potential and hence the ww_mutex_lock call will block and not
Documentation/locking/ww-mutex-design.rst-87- prematurely return -EDEADLK. The advantage of the _slow functions is in
--
Documentation/locking/ww-mutex-design.rst-89-
Documentation/locking/ww-mutex-design.rst:90: - ww_mutex_lock has a __must_check int return type, whereas ww_mutex_lock_slow
Documentation/locking/ww-mutex-design.rst-91- has a void return type. Note that since ww mutex code needs loops/retries
--
Documentation/locking/ww-mutex-design.rst-93- very first lock operation can never fail.
Documentation/locking/ww-mutex-design.rst:94: - When full debugging is enabled ww_mutex_lock_slow checks that all acquired
Documentation/locking/ww-mutex-design.rst-95- ww mutex have been released (preventing deadlocks) and makes sure that we
--
Documentation/locking/ww-mutex-design.rst-99-* Functions to only acquire a single w/w mutex, which results in the exact same
Documentation/locking/ww-mutex-design.rst:100: semantics as a normal mutex. This is done by calling ww_mutex_lock with a NULL
Documentation/locking/ww-mutex-design.rst-101- context.
--
Documentation/locking/ww-mutex-design.rst=139=not have duplicate entries (e.g. for a gpu commandbuffer submission ioctl)::
--
Documentation/locking/ww-mutex-design.rst-154- }
Documentation/locking/ww-mutex-design.rst:155: ret = ww_mutex_lock(&entry->obj->lock, ctx);
Documentation/locking/ww-mutex-design.rst-156- if (ret < 0) {
--
Documentation/locking/ww-mutex-design.rst-173- /* we lost out in a seqno race, lock and retry.. */
Documentation/locking/ww-mutex-design.rst:174: ww_mutex_lock_slow(&contended_entry->obj->lock, ctx);
Documentation/locking/ww-mutex-design.rst-175- res_obj = contended_entry->obj;
--
Documentation/locking/ww-mutex-design.rst=185=list-reordering allows for a bit more idiomatic code::
--
Documentation/locking/ww-mutex-design.rst-193- list_for_each_entry (entry, list, head) {
Documentation/locking/ww-mutex-design.rst:194: ret = ww_mutex_lock(&entry->obj->lock, ctx);
Documentation/locking/ww-mutex-design.rst-195- if (ret < 0) {
--
Documentation/locking/ww-mutex-design.rst-206- /* we lost out in a seqno race, lock and retry.. */
Documentation/locking/ww-mutex-design.rst:207: ww_mutex_lock_slow(&entry->obj->lock, ctx);
Documentation/locking/ww-mutex-design.rst-208-
--
Documentation/locking/ww-mutex-design.rst=267=variants, but that's outside of the scope of these examples here::
--
Documentation/locking/ww-mutex-design.rst-299-
Documentation/locking/ww-mutex-design.rst:300: ret = ww_mutex_lock(obj->ww_mutex, ctx);
Documentation/locking/ww-mutex-design.rst-301- if (ret == -EALREADY) {
--
Documentation/locking/ww-mutex-design.rst-307-
Documentation/locking/ww-mutex-design.rst:308: ww_mutex_lock_slow(obj, ctx);
Documentation/locking/ww-mutex-design.rst-309- list_add(&entry->locked_list, list);
--
Documentation/locking/ww-mutex-design.rst=364=Lockdep:
--
Documentation/locking/ww-mutex-design.rst-378-
Documentation/locking/ww-mutex-design.rst:379: - Calling ww_mutex_lock_slow before -EDEADLK was returned.
Documentation/locking/ww-mutex-design.rst-380-
--
Documentation/process/maintainer-tip.rst=231=scenario::
--
Documentation/process/maintainer-tip.rst-233- CPU0 CPU1
Documentation/process/maintainer-tip.rst:234: rtmutex_lock(&rcu->rt_mutex)
Documentation/process/maintainer-tip.rst-235- spin_lock(&rcu->rt_mutex.wait_lock)
--
Documentation/trace/ftrace.rst=3030=listed in:
--
Documentation/trace/ftrace.rst-3041- pick_next_task_fair
Documentation/trace/ftrace.rst:3042: mutex_lock
Documentation/trace/ftrace.rst-3043- [...]
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=157=allora, per proteggerla, potete utilizzare un semplice mutex
Documentation/translations/it_IT/kernel-hacking/locking.rst-158-(``include/linux/mutex.h``). Questo è il caso più semplice: inizializzate il
Documentation/translations/it_IT/kernel-hacking/locking.rst:159:mutex; invocate mutex_lock_interruptible() per trattenerlo e
Documentation/translations/it_IT/kernel-hacking/locking.rst:160:mutex_unlock() per rilasciarlo. C'è anche mutex_lock()
Documentation/translations/it_IT/kernel-hacking/locking.rst-161-ma questa dovrebbe essere evitata perché non ritorna in caso di segnali.
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=352=Table: Tabella dei requisiti per la sincronizzazione
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-362-+--------+----------------------------+
Documentation/translations/it_IT/kernel-hacking/locking.rst:363:| MLI | mutex_lock_interruptible |
Documentation/translations/it_IT/kernel-hacking/locking.rst-364-+--------+----------------------------+
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=403=e tutti gli oggetti che contiene. Ecco il codice::
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-471-
Documentation/translations/it_IT/kernel-hacking/locking.rst:472: mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-473- __cache_add(obj);
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-479- {
Documentation/translations/it_IT/kernel-hacking/locking.rst:480: mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-481- __cache_delete(__cache_find(id));
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-489-
Documentation/translations/it_IT/kernel-hacking/locking.rst:490: mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-491- obj = __cache_find(id);
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=519=sono quelle rimosse, mentre quelle ``+`` sono quelle aggiunte.
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-545-
Documentation/translations/it_IT/kernel-hacking/locking.rst:546: - mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-547- + spin_lock_irqsave(&cache_lock, flags);
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-555- {
Documentation/translations/it_IT/kernel-hacking/locking.rst:556: - mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-557- + unsigned long flags;
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-570-
Documentation/translations/it_IT/kernel-hacking/locking.rst:571: - mutex_lock(&cache_lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-572- + spin_lock_irqsave(&cache_lock, flags);
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=1312=da un'interruzione software. Il gestore d'interruzione non utilizza alcun
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-1314-
Documentation/translations/it_IT/kernel-hacking/locking.rst:1315: mutex_lock(&lock);
Documentation/translations/it_IT/kernel-hacking/locking.rst-1316- disable_irq(irq);
--
Documentation/translations/it_IT/kernel-hacking/locking.rst=1346=dormire.
--
Documentation/translations/it_IT/kernel-hacking/locking.rst-1359-
Documentation/translations/it_IT/kernel-hacking/locking.rst:1360:- mutex_lock_interruptible() and
Documentation/translations/it_IT/kernel-hacking/locking.rst:1361: mutex_lock()
Documentation/translations/it_IT/kernel-hacking/locking.rst-1362-
--
Documentation/translations/it_IT/locking/lockdep-design.rst=61=graffe, per un totale di `2 * n` (`n`: bit STATO). Un esempio inventato::
--
Documentation/translations/it_IT/locking/lockdep-design.rst-63- modprobe/2287 is trying to acquire lock:
Documentation/translations/it_IT/locking/lockdep-design.rst:64: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/translations/it_IT/locking/lockdep-design.rst-65-
Documentation/translations/it_IT/locking/lockdep-design.rst-66- but task is already holding lock:
Documentation/translations/it_IT/locking/lockdep-design.rst:67: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/translations/it_IT/locking/lockdep-design.rst-68-
--
Documentation/translations/it_IT/locking/lockdep-design.rst=80=Il seguente esempio mostra i bit::
Documentation/translations/it_IT/locking/lockdep-design.rst-81-
Documentation/translations/it_IT/locking/lockdep-design.rst:82: (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
Documentation/translations/it_IT/locking/lockdep-design.rst-83- ||||
--
Documentation/translations/it_IT/locking/lockdep-design.rst=211=chiamata simile a::
Documentation/translations/it_IT/locking/lockdep-design.rst-212-
Documentation/translations/it_IT/locking/lockdep-design.rst:213: enum bdev_bd_mutex_lock_class
Documentation/translations/it_IT/locking/lockdep-design.rst-214- {
--
Documentation/translations/it_IT/locking/lockdep-design.rst-219-
Documentation/translations/it_IT/locking/lockdep-design.rst:220: mutex_lock_nested(&bdev->bd_contains->bd_mutex, BD_MUTEX_PARTITION);
Documentation/translations/it_IT/locking/lockdep-design.rst-221-
--
Documentation/translations/it_IT/locking/locktorture.rst=50=torture_type
--
Documentation/translations/it_IT/locking/locktorture.rst-71-
Documentation/translations/it_IT/locking/locktorture.rst:72: - "mutex_lock":
Documentation/translations/it_IT/locking/locktorture.rst:73: coppie di mutex_lock() e mutex_unlock().
Documentation/translations/it_IT/locking/locktorture.rst-74-
Documentation/translations/it_IT/locking/locktorture.rst:75: - "rtmutex_lock":
Documentation/translations/it_IT/locking/locktorture.rst:76: coppie di rtmutex_lock() e rtmutex_unlock().
Documentation/translations/it_IT/locking/locktorture.rst-77- Il kernel deve avere CONFIG_RT_MUTEXES=y.
--
Documentation/translations/zh_CN/core-api/kref.rst=154=put::
--
Documentation/translations/zh_CN/core-api/kref.rst-182- struct my_data *entry = NULL;
Documentation/translations/zh_CN/core-api/kref.rst:183: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-184- if (!list_empty(&q)) {
--
Documentation/translations/zh_CN/core-api/kref.rst-201- {
Documentation/translations/zh_CN/core-api/kref.rst:202: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-203- kref_put(&entry->refcount, release_entry);
--
Documentation/translations/zh_CN/core-api/kref.rst-217- {
Documentation/translations/zh_CN/core-api/kref.rst:218: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-219- if (kref_put(&entry->refcount, release_entry)) {
--
Documentation/translations/zh_CN/core-api/kref.rst-235- struct my_data *entry = NULL;
Documentation/translations/zh_CN/core-api/kref.rst:236: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-237- if (!list_empty(&q)) {
--
Documentation/translations/zh_CN/core-api/kref.rst-249-
Documentation/translations/zh_CN/core-api/kref.rst:250: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-251- list_del(&entry->link);
--
Documentation/translations/zh_CN/core-api/kref.rst=267=Krefs和RCU
--
Documentation/translations/zh_CN/core-api/kref.rst-297-
Documentation/translations/zh_CN/core-api/kref.rst:298: mutex_lock(&mutex);
Documentation/translations/zh_CN/core-api/kref.rst-299- list_del_rcu(&entry->link);
--
Documentation/translations/zh_CN/core-api/refcount-vs-atomic.rst=55=smp_acquire__after_ctrl_dep()实现的。
--
Documentation/translations/zh_CN/core-api/refcount-vs-atomic.rst-151- * atomic_dec_and_lock() --> refcount_dec_and_lock()
Documentation/translations/zh_CN/core-api/refcount-vs-atomic.rst:152: * atomic_dec_and_mutex_lock() --> refcount_dec_and_mutex_lock()
Documentation/translations/zh_CN/core-api/refcount-vs-atomic.rst-153-
--
Documentation/translations/zh_CN/locking/mutex-design.rst=37=kernel/locking/mutex.c中实现。这些锁使用一个原子变量(->owner)来跟踪
--
Documentation/translations/zh_CN/locking/mutex-design.rst-110-
Documentation/translations/zh_CN/locking/mutex-design.rst:111: void mutex_lock(struct mutex *lock);
Documentation/translations/zh_CN/locking/mutex-design.rst:112: void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
Documentation/translations/zh_CN/locking/mutex-design.rst-113- int mutex_trylock(struct mutex *lock);
--
Documentation/translations/zh_CN/locking/mutex-design.rst-116-
Documentation/translations/zh_CN/locking/mutex-design.rst:117: int mutex_lock_interruptible_nested(struct mutex *lock,
Documentation/translations/zh_CN/locking/mutex-design.rst-118- unsigned int subclass);
Documentation/translations/zh_CN/locking/mutex-design.rst:119: int mutex_lock_interruptible(struct mutex *lock);
Documentation/translations/zh_CN/locking/mutex-design.rst-120-
--
Documentation/translations/zh_CN/locking/mutex-design.rst-122-
Documentation/translations/zh_CN/locking/mutex-design.rst:123: int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
Documentation/translations/zh_CN/locking/mutex-design.rst-124-
--
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt=546=release()回调必须被设置,且在最后一个 video_device 用户退出之后
--
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-564-- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt:565: 就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-566- 在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后
--
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt=612=v4l2_file_operations 与锁
--
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-614-
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt:615:你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-616-这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁
--
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-627-
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt:628:如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现
Documentation/translations/zh_CN/video4linux/v4l2-framework.txt-629-锁机制。
--
Documentation/virt/kvm/locking.rst=38=For SRCU:
--
Documentation/virt/kvm/locking.rst-45- srcu_read_lock(&kvm->srcu);
Documentation/virt/kvm/locking.rst:46: mutex_lock(&kvm->slots_lock);
Documentation/virt/kvm/locking.rst-47-
--
arch/arm/common/bL_switcher.c=550=static int bL_switcher_enable(void)
--
arch/arm/common/bL_switcher.c-553-
arch/arm/common/bL_switcher.c:554: mutex_lock(&bL_switcher_activation_lock);
arch/arm/common/bL_switcher.c-555- lock_device_hotplug();
--
arch/arm/common/bL_switcher.c=600=static void bL_switcher_disable(void)
--
arch/arm/common/bL_switcher.c-605-
arch/arm/common/bL_switcher.c:606: mutex_lock(&bL_switcher_activation_lock);
arch/arm/common/bL_switcher.c-607- lock_device_hotplug();
--
arch/arm/common/bL_switcher.c=734=bool bL_switcher_get_enabled(void)
arch/arm/common/bL_switcher.c-735-{
arch/arm/common/bL_switcher.c:736: mutex_lock(&bL_switcher_activation_lock);
arch/arm/common/bL_switcher.c-737-
--
arch/arm/mach-mxs/mach-mxs.c=86=static const u32 *mxs_get_ocotp(void)
--
arch/arm/mach-mxs/mach-mxs.c-100-
arch/arm/mach-mxs/mach-mxs.c:101: mutex_lock(&ocotp_mutex);
arch/arm/mach-mxs/mach-mxs.c-102-
--
arch/arm/mach-omap2/omap_hwmod.c=3513=int omap_hwmod_init_module(struct device *dev,
--
arch/arm/mach-omap2/omap_hwmod.c-3547- oh->class->name = data->name;
arch/arm/mach-omap2/omap_hwmod.c:3548: mutex_lock(&list_lock);
arch/arm/mach-omap2/omap_hwmod.c-3549- error = _register(oh);
--
arch/arm/mach-rpc/ecard.c=292=static void ecard_call(struct ecard_request *req)
--
arch/arm/mach-rpc/ecard.c-297-
arch/arm/mach-rpc/ecard.c:298: mutex_lock(&ecard_mutex);
arch/arm/mach-rpc/ecard.c-299- ecard_req = req;
--
arch/arm64/kernel/armv8_deprecated.c=510=static int emulation_proc_handler(const struct ctl_table *table, int write,
--
arch/arm64/kernel/armv8_deprecated.c-517-
arch/arm64/kernel/armv8_deprecated.c:518: mutex_lock(&insn_emulation_mutex);
arch/arm64/kernel/armv8_deprecated.c-519- ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
--
arch/arm64/kvm/arch_timer.c=1530=static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu)
--
arch/arm64/kvm/arch_timer.c-1534-
arch/arm64/kvm/arch_timer.c:1535: mutex_lock(&vcpu->kvm->arch.config_lock);
arch/arm64/kvm/arch_timer.c-1536-
--
arch/arm64/kvm/arch_timer.c=1735=int kvm_vm_ioctl_set_counter_offset(struct kvm *kvm,
--
arch/arm64/kvm/arch_timer.c-1745-
arch/arm64/kvm/arch_timer.c:1746: mutex_lock(&kvm->lock);
arch/arm64/kvm/arch_timer.c-1747-
--
arch/arm64/kvm/arm.c=134=int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
--
arch/arm64/kvm/arm.c-151- case KVM_CAP_ARM_MTE:
arch/arm64/kvm/arm.c:152: mutex_lock(&kvm->lock);
arch/arm64/kvm/arm.c-153- if (system_supports_mte() && !kvm->created_vcpus) {
--
arch/arm64/kvm/arm.c-163- case KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE:
arch/arm64/kvm/arm.c:164: mutex_lock(&kvm->slots_lock);
arch/arm64/kvm/arm.c-165- /*
--
arch/arm64/kvm/arm.c-179- case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS:
arch/arm64/kvm/arm.c:180: mutex_lock(&kvm->lock);
arch/arm64/kvm/arm.c-181- if (!kvm->created_vcpus) {
--
arch/arm64/kvm/arm.c=208=int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
--
]
|
| 18/2 |
2026/06/01 06:16 |
llm |
expert |
0m
Model:
Tokens:
input: 62715
output: 22
thoughts: 1418
Thoughts: **Analyzing a Concurrency Issue**
Okay, so I'm looking at this trace and trying to piece together what's going on. First, the `mutex_lock` isn't anything special. Let's dig back into `nfsd_mutex`... if it's held by a task not in the lockdep output, that would be weird, but it IS a lock, so it *should* be in the output. Could it be `khungtaskd`? No, it holds `rcu_read_lock`. What about this `syz-executor/7333`? Aha! It holds `rtnl_mutex`, `wg->device_update_lock`, and `rcu_state.exp_mutex`.
And it's on CPU 0. The NMI backtrace shows it in `mark_held_locks`, then trace from `genl_rcv` all the way down to `console_trylock_spinning`. It looks like it's trying to print something, but spinning.
So, `syz-executor/7333` is in `genl_rcv` which calls `hwsim_new_radio_nl` and that calls `ieee80211_register_hw`. That eventually leads to the print. This function is holding the `cb_lock` for reading. Also, it holds `rtnl_mutex` according to its lockdep output, and that's from `rtnl_newlink` which leads to `wg_open` and `exp_funnel_lock`. But... the trace from NMI shows it in `genl_rcv`! So there is an issue with the backtrace. The lockdep output shows `rtnl_mutex` from `rtnl_newlink`.
Wait, the PID is the same. Maybe it's spinning on `console_trylock_spinning`? It is waiting for the console lock. *Why* is it spinning? Someone else is printing. Because it is in `genl_rcv`, it should be holding `cb_lock` in read mode. But it's not in the held locks list. Is the `cb_lock` tracked by lockdep? Yes, it's an rwsem. So, why isn't it showing up? Is it not *actually* holding `cb_lock`? I need to check the code... let's look at `hwsim_new_radio_nl`.
|
| 19/2 |
2026/06/01 06:16 |
tool |
grepper |
0m
Args: map[Expression:hwsim_new_radio_nl]
Results: map[Output:drivers/net/ieee802154/mac802154_hwsim.c=329=static const struct ieee802154_ops hwsim_ops = {
--
drivers/net/ieee802154/mac802154_hwsim.c-339-
drivers/net/ieee802154/mac802154_hwsim.c:340:static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
drivers/net/ieee802154/mac802154_hwsim.c-341-{
--
drivers/net/ieee802154/mac802154_hwsim.c=751=static const struct genl_small_ops hwsim_nl_ops[] = {
--
drivers/net/ieee802154/mac802154_hwsim.c-754- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
drivers/net/ieee802154/mac802154_hwsim.c:755: .doit = hwsim_new_radio_nl,
drivers/net/ieee802154/mac802154_hwsim.c-756- .flags = GENL_UNS_ADMIN_PERM,
--
drivers/net/wireless/virtual/mac80211_hwsim.c=6538=static int parse_pmsr_capa(const struct nlattr *pmsr_capa, struct cfg80211_pmsr_capabilities *out,
--
drivers/net/wireless/virtual/mac80211_hwsim.c-6576-
drivers/net/wireless/virtual/mac80211_hwsim.c:6577:static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
drivers/net/wireless/virtual/mac80211_hwsim.c-6578-{
--
drivers/net/wireless/virtual/mac80211_hwsim.c=6874=static const struct genl_small_ops hwsim_ops[] = {
--
drivers/net/wireless/virtual/mac80211_hwsim.c-6893- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
drivers/net/wireless/virtual/mac80211_hwsim.c:6894: .doit = hwsim_new_radio_nl,
drivers/net/wireless/virtual/mac80211_hwsim.c-6895- .flags = GENL_UNS_ADMIN_PERM,
]
|
| 20/2 |
2026/06/01 06:16 |
llm |
expert |
0m
Model:
Tokens:
input: 65425
output: 29
thoughts: 730
Thoughts: **Debugging a Potential Lock Contention Issue**
Okay, let's break this down. My initial thought was, "Aha! `hwsim_new_radio_nl` is triggered from `genl_family_rcv_msg_doit`, and that's down the call chain from `genl_rcv` via `netlink_rcv_skb`." This path is important because `genl_rcv` calls `down_read(&cb_lock)` *before* the call to `netlink_rcv_skb`. So, if `hwsim_new_radio_nl` is involved, then process `syz-executor/7333` *should* be holding `cb_lock`. That's where I hit the first snag, "Why isn't this apparent in the lockdep output?"
I then scrutinized the lockdep output for `syz-executor/7333` and, sure enough, it shows three locks: `rtnl_mutex`, `wg->device_update_lock`, and `rcu_state.exp_mutex`. But, crucially, `cb_lock` is *missing*. That's the anomaly.
Now, I considered a basic mistake: "Could there be two tasks with the same PID, `syz-executor/7333`?" The answer, of course, is a definitive "No," PID's are unique.
The next thought was, "Could the lockdep output and the NMI backtrace have been generated at different points in time?" I realized that the lockdep output comes from `debug_show_all_locks`, which is called by `check_hung_uninterruptible_tasks`, which then calls `trigger_all_cpu_backtrace`. Bingo! These are printed at slightly different times. That's a significant point.
But then I had another insight: "If `syz-executor/7333` is actively in `vprintk_emit` in the NMI backtrace, it *must* be currently running." It can't be a past state. Now I need to understand what `vprintk_emit` is doing. I remember that `vprintk_emit` is called from `ieee80211_init_rate_ctrl_alg.cold`. So, the next step is clearly to go deep on that function. I must understand how this function and the functions around it can be related to the locks reported.
|
| 21/2 |
2026/06/01 06:16 |
tool |
grepper |
0m
Args: map[Expression:ieee80211_init_rate_ctrl_alg]
Results: map[Output:net/mac80211/main.c=1134=int ieee80211_register_hw(struct ieee80211_hw *hw)
--
net/mac80211/main.c-1558- rtnl_lock();
net/mac80211/main.c:1559: result = ieee80211_init_rate_ctrl_alg(local,
net/mac80211/main.c-1560- hw->rate_control_algorithm);
--
net/mac80211/rate.c=998=EXPORT_SYMBOL(rate_control_set_rates);
net/mac80211/rate.c-999-
net/mac80211/rate.c:1000:int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
net/mac80211/rate.c-1001- const char *name)
--
net/mac80211/rate.h=88=void ieee80211_check_rate_mask(struct ieee80211_link_data *link);
--
net/mac80211/rate.h-91- * first available algorithm. */
net/mac80211/rate.h:92:int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
net/mac80211/rate.h-93- const char *name);
]
|
| 22/2 |
2026/06/01 06:16 |
llm |
expert |
0m
Model:
Tokens:
input: 67137
output: 41
thoughts: 2045
Thoughts: **Analyzing a Potential Kernel Lock Issue**
Okay, let's break this down. First, I noticed that `ieee80211_register_hw` calls `rtnl_lock()` before calling a rate control algorithm initialization function. My immediate thought was to see if `rtnl_mutex` was causing the issue. A `syz-executor` task (specifically, `syz-executor/7333`) seemed to be holding this mutex according to the lockdep output.
The lockdep output revealed that `rtnl_mutex` was acquired at `rtnl_newlink+0x8bb/0x2380`, which seemed odd because `ieee80211_register_hw` acquires it at a different location. I started to wonder if there were multiple `syz-executor` tasks involved. The NMI backtrace from CPU 0 showed `syz-executor/7333`. That specific `syz-executor` was definitely holding `rtnl_mutex`.
The backtrace in the NMI, however, did not have the `rtnl_newlink` function. The backtrace started from `hwsim_new_radio_nl`, which calls `genl_family_rcv_msg_doit`. I had the thought that maybe `rtnl_newlink` acquired the lock and then passed it to a function that didn't release it, but that's impossible with a mutex. I then considered whether the task might have moved past acquiring the lock before the NMI backtrace was taken. But the NMI is taken *after* the lockdep output.
If the task was no longer blocked on `rtnl_mutex`, then it would have released the lock and probably acquired `cb_lock`. However, `check_hung_uninterruptible_tasks` takes the lockdep output and then triggers the NMI. Since the task `syz-executor/7333` was running on CPU 0, it wasn't hung! The actual hung tasks, `syz.3.20:5999`, `syz.1.18:6009`, etc., are blocked on `nfsd_mutex` in `nfsd_nl_threads_set_doit`. That's the real problem.
So, who's holding `nfsd_mutex`? I looked at the lockdep output again. Many tasks, approximately 30, were listed as holding `nfsd_mutex`. This is a classic indicator: If a task has a lock, it will show up in "locks held by ...". But the list indicated they were ALL blocked. This meant NONE of them actually *held* it in the sense of being able to progress. They were all waiting at `nfsd_nl_threads_set_doit+0x8ec/0x12b0`, which is the line that corresponds to `mutex_lock(&nfsd_mutex);`. If one *did* have the lock, its backtrace would be different. This pointed to the root cause: *none* of them actually got past the `mutex_lock`.
The question became, is the holder of `nfsd_mutex` one of these 30 tasks? What if it's some *other* task? I searched the entire lockdep output and found no other task holding `nfsd_mutex`. If a task had the mutex and no other locks, it would be listed. It was also unlikely that a task had acquired it and exited, because lockdep reports on tasks exiting with held locks. The reports didn't indicate such a warning, although it was possible it happened earlier.
Finally, `nfsd_nl_threads_set_doit` allocates memory via `kcalloc` and then tries to lock the mutex. My next step is to examine the function call.
|
| 23/2 |
2026/06/01 06:17 |
tool |
read-file |
0m
Args: map[File:fs/nfsd/nfsctl.c FirstLine:1617 LineCount:100]
Results: map[Contents:1617: int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
1618: {
1619: int *nthreads, nrpools = 0, i, ret = -EOPNOTSUPP, rem;
1620: struct net *net = genl_info_net(info);
1621: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1622: const struct nlattr *attr;
1623: const char *scope = NULL;
1624:
1625: if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_THREADS))
1626: return -EINVAL;
1627:
1628: /* count number of SERVER_THREADS values */
1629: nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
1630: GENL_HDRLEN, rem)
1631: nrpools++;
1632:
1633: mutex_lock(&nfsd_mutex);
1634:
1635: nthreads = kzalloc_objs(int, nrpools);
1636: if (!nthreads) {
1637: ret = -ENOMEM;
1638: goto out_unlock;
1639: }
1640:
1641: i = 0;
1642: nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
1643: GENL_HDRLEN, rem) {
1644: nthreads[i++] = nla_get_u32(attr);
1645: if (i >= nrpools)
1646: break;
1647: }
1648:
1649: if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
1650: info->attrs[NFSD_A_SERVER_LEASETIME] ||
1651: info->attrs[NFSD_A_SERVER_SCOPE] ||
1652: info->attrs[NFSD_A_SERVER_FH_KEY]) {
1653: ret = -EBUSY;
1654: if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads)
1655: goto out_unlock;
1656:
1657: ret = -EINVAL;
1658: attr = info->attrs[NFSD_A_SERVER_GRACETIME];
1659: if (attr) {
1660: u32 gracetime = nla_get_u32(attr);
1661:
1662: if (gracetime < 10 || gracetime > 3600)
1663: goto out_unlock;
1664:
1665: nn->nfsd4_grace = gracetime;
1666: }
1667:
1668: attr = info->attrs[NFSD_A_SERVER_LEASETIME];
1669: if (attr) {
1670: u32 leasetime = nla_get_u32(attr);
1671:
1672: if (leasetime < 10 || leasetime > 3600)
1673: goto out_unlock;
1674:
1675: nn->nfsd4_lease = leasetime;
1676: }
1677:
1678: attr = info->attrs[NFSD_A_SERVER_SCOPE];
1679: if (attr)
1680: scope = nla_data(attr);
1681:
1682: attr = info->attrs[NFSD_A_SERVER_FH_KEY];
1683: if (attr) {
1684: ret = nfsd_nl_fh_key_set(attr, nn);
1685: trace_nfsd_ctl_fh_key_set((const char *)nn->fh_key, ret);
1686: if (ret)
1687: goto out_unlock;
1688: }
1689: }
1690:
1691: attr = info->attrs[NFSD_A_SERVER_MIN_THREADS];
1692: if (attr)
1693: nn->min_threads = nla_get_u32(attr);
1694:
1695: ret = nfsd_svc(nrpools, nthreads, net, current_cred(), scope);
1696: if (ret > 0)
1697: ret = 0;
1698: out_unlock:
1699: mutex_unlock(&nfsd_mutex);
1700: kfree(nthreads);
1701: return ret;
1702: }
1703:
1704: /**
1705: * nfsd_nl_threads_get_doit - get the maximum number of running threads
1706: * @skb: reply buffer
1707: * @info: netlink metadata and command arguments
1708: *
1709: * Return 0 on success or a negative errno.
1710: */
1711: int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
1712: {
1713: struct net *net = genl_info_net(info);
1714: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
1715: void *hdr;
1716: int err;
]
|
| 24/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 71654
output: 18
thoughts: 21
Thoughts: Here's my thought process, summarized as if I were considering the implications of seeing `kzalloc_objs`:
**Initial Assessment: Unfamiliar Terrain**
Okay, wait a minute... I see `kzalloc_objs`. That's not a common function. My first instinct is to pause. I need to understand what this `kzalloc_objs` thing is. It's a key function in a critical area. My expert background has provided me with a foundation that demands a quick understanding of this function. I need to identify it's purpose, impact, and associated risks.
|
| 25/2 |
2026/06/01 06:17 |
tool |
grepper |
0m
Args: map[Expression:kzalloc_objs]
Results: map[Output:
Full output is too long, showing 500 out of 13919 lines.
Use more precise expression if possible.
Documentation/process/deprecated.rst=393=become, respectively::
--
Documentation/process/deprecated.rst-397- ptr = kmalloc_objs(*ptr, count, gfp);
Documentation/process/deprecated.rst:398: ptr = kzalloc_objs(*ptr, count, gfp);
Documentation/process/deprecated.rst-399- ptr = kmalloc_flex(*ptr, flex_member, count, gfp);
--
arch/alpha/kernel/module.c=64=module_frob_arch_sections(Elf64_Ehdr *hdr, Elf64_Shdr *sechdrs,
--
arch/alpha/kernel/module.c-95- nsyms = symtab->sh_size / sizeof(Elf64_Sym);
arch/alpha/kernel/module.c:96: chains = kzalloc_objs(struct got_entry, nsyms);
arch/alpha/kernel/module.c-97- if (!chains) {
--
arch/arm/kernel/vdso.c=169=static int __init vdso_init(void)
--
arch/arm/kernel/vdso.c-181- /* Allocate the VDSO text pagelist */
arch/arm/kernel/vdso.c:182: vdso_text_pagelist = kzalloc_objs(struct page *, text_pages);
arch/arm/kernel/vdso.c-183- if (vdso_text_pagelist == NULL)
--
arch/arm/mach-footbridge/dc21285.c=261=int __init dc21285_setup(int nr, struct pci_sys_data *sys)
--
arch/arm/mach-footbridge/dc21285.c-264-
arch/arm/mach-footbridge/dc21285.c:265: res = kzalloc_objs(struct resource, 2);
arch/arm/mach-footbridge/dc21285.c-266- if (!res) {
--
arch/arm/mach-omap1/mcbsp.c=292=static void omap_mcbsp_register_board_cfg(struct resource *res, int res_count,
--
arch/arm/mach-omap1/mcbsp.c-296-
arch/arm/mach-omap1/mcbsp.c:297: omap_mcbsp_devices = kzalloc_objs(struct platform_device *, size);
arch/arm/mach-omap1/mcbsp.c-298- if (!omap_mcbsp_devices) {
--
arch/arm/mach-omap2/omap_device.c=131=static int omap_device_build_from_dt(struct platform_device *pdev)
--
arch/arm/mach-omap2/omap_device.c-158-
arch/arm/mach-omap2/omap_device.c:159: hwmods = kzalloc_objs(struct omap_hwmod *, oh_cnt);
arch/arm/mach-omap2/omap_device.c-160- if (!hwmods) {
--
arch/arm/mach-omap2/pm33xx-core.c=379=static int __init amx3_idle_init(struct device_node *cpu_node, int cpu)
--
arch/arm/mach-omap2/pm33xx-core.c-412-
arch/arm/mach-omap2/pm33xx-core.c:413: idle_states = kzalloc_objs(*idle_states, state_count);
arch/arm/mach-omap2/pm33xx-core.c-414- if (!idle_states)
--
arch/arm/mach-omap2/sr_device.c=30=static void __init sr_set_nvalues(struct omap_volt_data *volt_data,
--
arch/arm/mach-omap2/sr_device.c-41-
arch/arm/mach-omap2/sr_device.c:42: nvalue_table = kzalloc_objs(*nvalue_table, count);
arch/arm/mach-omap2/sr_device.c-43- if (!nvalue_table)
--
arch/arm/mach-versatile/spc.c=393=static int ve_spc_populate_opps(uint32_t cluster)
--
arch/arm/mach-versatile/spc.c-397-
arch/arm/mach-versatile/spc.c:398: opps = kzalloc_objs(*opps, MAX_OPPS);
arch/arm/mach-versatile/spc.c-399- if (!opps)
--
arch/arm/xen/enlighten.c=316=int __init arch_xen_unpopulated_init(struct resource **res)
--
arch/arm/xen/enlighten.c-343-
arch/arm/xen/enlighten.c:344: regs = kzalloc_objs(*regs, nr_reg);
arch/arm/xen/enlighten.c-345- if (!regs) {
--
arch/arm64/kernel/vdso.c=68=static int __init __vdso_init(enum vdso_abi abi)
--
arch/arm64/kernel/vdso.c-83-
arch/arm64/kernel/vdso.c:84: vdso_pagelist = kzalloc_objs(struct page *, vdso_info[abi].vdso_pages);
arch/arm64/kernel/vdso.c-85- if (vdso_pagelist == NULL)
--
arch/arm64/kvm/vgic/vgic-init.c=207=static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
--
arch/arm64/kvm/vgic/vgic-init.c-213- dist->active_spis = (atomic_t)ATOMIC_INIT(0);
arch/arm64/kvm/vgic/vgic-init.c:214: dist->spis = kzalloc_objs(struct vgic_irq, nr_spis, GFP_KERNEL_ACCOUNT);
arch/arm64/kvm/vgic/vgic-init.c-215- if (!dist->spis)
--
arch/arm64/kvm/vgic/vgic-init.c=329=static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type)
--
arch/arm64/kvm/vgic/vgic-init.c-344-
arch/arm64/kvm/vgic/vgic-init.c:345: vgic_cpu->private_irqs = kzalloc_objs(struct vgic_irq,
arch/arm64/kvm/vgic/vgic-init.c-346- num_private_irqs,
--
arch/arm64/kvm/vgic/vgic-irqfd.c=136=int kvm_vgic_setup_default_irq_routing(struct kvm *kvm)
--
arch/arm64/kvm/vgic/vgic-irqfd.c-142-
arch/arm64/kvm/vgic/vgic-irqfd.c:143: entries = kzalloc_objs(*entries, nr, GFP_KERNEL_ACCOUNT);
arch/arm64/kvm/vgic/vgic-irqfd.c-144- if (!entries)
--
arch/arm64/kvm/vgic/vgic-v4.c=242=int vgic_v4_init(struct kvm *kvm)
--
arch/arm64/kvm/vgic/vgic-v4.c-258-
arch/arm64/kvm/vgic/vgic-v4.c:259: dist->its_vm.vpes = kzalloc_objs(*dist->its_vm.vpes, nr_vcpus,
arch/arm64/kvm/vgic/vgic-v4.c-260- GFP_KERNEL_ACCOUNT);
--
arch/csky/kernel/vdso.c=17=static int __init vdso_init(void)
--
arch/csky/kernel/vdso.c-22- vdso_pagelist =
arch/csky/kernel/vdso.c:23: kzalloc_objs(struct page *, vdso_pages);
arch/csky/kernel/vdso.c-24- if (unlikely(vdso_pagelist == NULL)) {
--
arch/loongarch/kernel/vdso.c=45=static int __init init_vdso(void)
--
arch/loongarch/kernel/vdso.c-55- vdso_info.code_mapping.pages =
arch/loongarch/kernel/vdso.c:56: kzalloc_objs(struct page *, vdso_info.size / PAGE_SIZE);
arch/loongarch/kernel/vdso.c-57-
--
arch/loongarch/kvm/intc/pch_pic.c=410=static int kvm_setup_default_irq_routing(struct kvm *kvm)
--
arch/loongarch/kvm/intc/pch_pic.c-415-
arch/loongarch/kvm/intc/pch_pic.c:416: entries = kzalloc_objs(*entries, nr);
arch/loongarch/kvm/intc/pch_pic.c-417- if (!entries)
--
arch/mips/alchemy/common/clock.c=754=static int __init alchemy_clk_init_fgens(int ctype)
--
arch/mips/alchemy/common/clock.c-777-
arch/mips/alchemy/common/clock.c:778: a = kzalloc_objs(*a, 6);
arch/mips/alchemy/common/clock.c-779- if (!a)
--
arch/mips/alchemy/common/clock.c=960=static int __init alchemy_clk_setup_imux(int ctype)
--
arch/mips/alchemy/common/clock.c-998-
arch/mips/alchemy/common/clock.c:999: a = kzalloc_objs(*a, 6);
arch/mips/alchemy/common/clock.c-1000- if (!a)
--
arch/mips/alchemy/common/dbdma.c=1056=static int __init dbdma_setup(unsigned int irq, dbdev_tab_t *idtable)
--
arch/mips/alchemy/common/dbdma.c-1059-
arch/mips/alchemy/common/dbdma.c:1060: dbdev_tab = kzalloc_objs(dbdev_tab_t, DBDEV_TAB_SIZE);
arch/mips/alchemy/common/dbdma.c-1061- if (!dbdev_tab)
--
arch/mips/alchemy/common/platform.c=203=static int __init _new_usbres(struct resource **r, struct platform_device **d)
arch/mips/alchemy/common/platform.c-204-{
arch/mips/alchemy/common/platform.c:205: *r = kzalloc_objs(struct resource, 2);
arch/mips/alchemy/common/platform.c-206- if (!*r)
--
arch/mips/alchemy/devboards/platform.c=68=int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start,
--
arch/mips/alchemy/devboards/platform.c-89-
arch/mips/alchemy/devboards/platform.c:90: sr = kzalloc_objs(struct resource, cnt);
arch/mips/alchemy/devboards/platform.c-91- if (!sr)
--
arch/mips/alchemy/devboards/platform.c=152=int __init db1x_register_norflash(unsigned long size, int width,
--
arch/mips/alchemy/devboards/platform.c-164- ret = -ENOMEM;
arch/mips/alchemy/devboards/platform.c:165: parts = kzalloc_objs(struct mtd_partition, 5);
arch/mips/alchemy/devboards/platform.c-166- if (!parts)
--
arch/mips/kernel/smp-cps.c=295=static void __init cps_prepare_cpus(unsigned int max_cpus)
--
arch/mips/kernel/smp-cps.c-343- nclusters = mips_cps_numclusters();
arch/mips/kernel/smp-cps.c:344: mips_cps_cluster_bootcfg = kzalloc_objs(*mips_cps_cluster_bootcfg,
arch/mips/kernel/smp-cps.c-345- nclusters);
--
arch/mips/kernel/smp-cps.c-354- ncores = mips_cps_numcores(cl);
arch/mips/kernel/smp-cps.c:355: core_bootcfg = kzalloc_objs(*core_bootcfg, ncores);
arch/mips/kernel/smp-cps.c-356- if (!core_bootcfg)
--
arch/mips/kernel/smp-cps.c-369- core_vpes = core_vpe_count(cl, c);
arch/mips/kernel/smp-cps.c:370: core_bootcfg[c].vpe_config = kzalloc_objs(*core_bootcfg[c].vpe_config,
arch/mips/kernel/smp-cps.c-371- core_vpes);
--
arch/parisc/kernel/vdso.c=99=static struct page ** __init vdso_setup_pages(void *start, void *end)
--
arch/parisc/kernel/vdso.c-104-
arch/parisc/kernel/vdso.c:105: pagelist = kzalloc_objs(struct page *, pages + 1);
arch/parisc/kernel/vdso.c-106- if (!pagelist)
--
arch/parisc/net/bpf_jit_core.c=44=struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
--
arch/parisc/net/bpf_jit_core.c-71- ctx->prog = prog;
arch/parisc/net/bpf_jit_core.c:72: ctx->offset = kzalloc_objs(int, prog->len);
arch/parisc/net/bpf_jit_core.c-73- if (!ctx->offset)
--
arch/powerpc/kernel/smp.c=1113=void __init smp_prepare_cpus(unsigned int max_cpus)
--
arch/powerpc/kernel/smp.c-1174- */
arch/powerpc/kernel/smp.c:1175: chip_id_lookup_table = kzalloc_objs(int, idx);
arch/powerpc/kernel/smp.c-1176- if (chip_id_lookup_table)
--
arch/powerpc/kernel/vdso.c=242=static struct page ** __init vdso_setup_pages(void *start, void *end)
--
arch/powerpc/kernel/vdso.c-247-
arch/powerpc/kernel/vdso.c:248: pagelist = kzalloc_objs(struct page *, pages + 1);
arch/powerpc/kernel/vdso.c-249- if (!pagelist)
--
arch/powerpc/kvm/e500_mmu.c=731=int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu,
--
arch/powerpc/kvm/e500_mmu.c-794-
arch/powerpc/kvm/e500_mmu.c:795: privs[0] = kzalloc_objs(*privs[0], params.tlb_sizes[0]);
arch/powerpc/kvm/e500_mmu.c-796- if (!privs[0]) {
--
arch/powerpc/kvm/e500_mmu.c-800-
arch/powerpc/kvm/e500_mmu.c:801: privs[1] = kzalloc_objs(*privs[1], params.tlb_sizes[1]);
arch/powerpc/kvm/e500_mmu.c-802- if (!privs[1]) {
--
arch/powerpc/kvm/e500_mmu.c=898=int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
--
arch/powerpc/kvm/e500_mmu.c-922-
arch/powerpc/kvm/e500_mmu.c:923: vcpu_e500->gtlb_priv[0] = kzalloc_objs(struct tlbe_priv,
arch/powerpc/kvm/e500_mmu.c-924- vcpu_e500->gtlb_params[0].entries);
--
arch/powerpc/kvm/e500_mmu.c-927-
arch/powerpc/kvm/e500_mmu.c:928: vcpu_e500->gtlb_priv[1] = kzalloc_objs(struct tlbe_priv,
arch/powerpc/kvm/e500_mmu.c-929- vcpu_e500->gtlb_params[1].entries);
--
arch/powerpc/mm/drmem.c=425=static void __init init_drmem_v1_lmbs(const __be32 *prop)
--
arch/powerpc/mm/drmem.c-432-
arch/powerpc/mm/drmem.c:433: drmem_info->lmbs = kzalloc_objs(*lmb, drmem_info->n_lmbs);
arch/powerpc/mm/drmem.c-434- if (!drmem_info->lmbs)
--
arch/powerpc/mm/drmem.c=441=static void __init init_drmem_v2_lmbs(const __be32 *prop)
--
arch/powerpc/mm/drmem.c-459-
arch/powerpc/mm/drmem.c:460: drmem_info->lmbs = kzalloc_objs(*lmb, drmem_info->n_lmbs);
arch/powerpc/mm/drmem.c-461- if (!drmem_info->lmbs)
--
arch/powerpc/perf/imc-pmu.c=221=static int update_events_in_group(struct device_node *node, struct imc_pmu *pmu)
--
arch/powerpc/perf/imc-pmu.c-259- /* Allocate memory for the events */
arch/powerpc/perf/imc-pmu.c:260: pmu->events = kzalloc_objs(struct imc_events, ct);
arch/powerpc/perf/imc-pmu.c-261- if (!pmu->events) {
--
arch/powerpc/perf/imc-pmu.c-290- */
arch/powerpc/perf/imc-pmu.c:291: attrs = kzalloc_objs(struct attribute *, ((ct * 3) + 1));
arch/powerpc/perf/imc-pmu.c-292- if (!attrs) {
--
arch/powerpc/perf/imc-pmu.c=1526=static int init_nest_pmu_ref(void)
--
arch/powerpc/perf/imc-pmu.c-1529-
arch/powerpc/perf/imc-pmu.c:1530: nest_imc_refc = kzalloc_objs(*nest_imc_refc, num_possible_nodes());
arch/powerpc/perf/imc-pmu.c-1531-
--
arch/powerpc/perf/imc-pmu.c=1683=static int imc_mem_init(struct imc_pmu *pmu_ptr, struct device_node *parent,
--
arch/powerpc/perf/imc-pmu.c-1700- if (!per_nest_pmu_arr) {
arch/powerpc/perf/imc-pmu.c:1701: per_nest_pmu_arr = kzalloc_objs(struct imc_pmu *,
arch/powerpc/perf/imc-pmu.c-1702- get_max_nest_dev() + 1);
--
arch/powerpc/perf/imc-pmu.c-1714- nr_cores = DIV_ROUND_UP(num_possible_cpus(), threads_per_core);
arch/powerpc/perf/imc-pmu.c:1715: pmu_ptr->mem_info = kzalloc_objs(struct imc_mem_info, nr_cores);
arch/powerpc/perf/imc-pmu.c-1716-
--
arch/powerpc/perf/imc-pmu.c-1719-
arch/powerpc/perf/imc-pmu.c:1720: core_imc_refc = kzalloc_objs(struct imc_pmu_ref, nr_cores);
arch/powerpc/perf/imc-pmu.c-1721-
--
arch/powerpc/perf/imc-pmu.c-1752- nr_cores = DIV_ROUND_UP(num_possible_cpus(), threads_per_core);
arch/powerpc/perf/imc-pmu.c:1753: trace_imc_refc = kzalloc_objs(struct imc_pmu_ref, nr_cores);
arch/powerpc/perf/imc-pmu.c-1754- if (!trace_imc_refc)
--
arch/powerpc/perf/vpa-dtl.c=508=static void *vpa_dtl_setup_aux(struct perf_event *event, void **pages,
--
arch/powerpc/perf/vpa-dtl.c-525-
arch/powerpc/perf/vpa-dtl.c:526: pglist = kzalloc_objs(*pglist, nr_pages);
arch/powerpc/perf/vpa-dtl.c-527- if (!pglist)
--
arch/powerpc/platforms/44x/pci.c=1307=static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
--
arch/powerpc/platforms/44x/pci.c-1341- ppc4xx_pciex_ports =
arch/powerpc/platforms/44x/pci.c:1342: kzalloc_objs(struct ppc4xx_pciex_port, count);
arch/powerpc/platforms/44x/pci.c-1343- if (ppc4xx_pciex_ports) {
--
arch/powerpc/platforms/powernv/idle.c=1322=static int __init pnv_parse_cpuidle_dt(void)
--
arch/powerpc/platforms/powernv/idle.c-1338-
arch/powerpc/platforms/powernv/idle.c:1339: pnv_idle_states = kzalloc_objs(*pnv_idle_states, nr_idle_states);
arch/powerpc/platforms/powernv/idle.c-1340- temp_u32 = kcalloc(nr_idle_states, sizeof(u32), GFP_KERNEL);
--
arch/powerpc/platforms/powernv/memtrace.c=131=static int memtrace_init_regions_runtime(u64 size)
--
arch/powerpc/platforms/powernv/memtrace.c-135-
arch/powerpc/platforms/powernv/memtrace.c:136: memtrace_array = kzalloc_objs(struct memtrace_entry, num_online_nodes());
arch/powerpc/platforms/powernv/memtrace.c-137- if (!memtrace_array) {
--
arch/powerpc/platforms/powernv/opal-async.c=246=int __init opal_async_comp_init(void)
--
arch/powerpc/platforms/powernv/opal-async.c-267- opal_max_async_tokens = be32_to_cpup(async);
arch/powerpc/platforms/powernv/opal-async.c:268: opal_async_tokens = kzalloc_objs(*opal_async_tokens,
arch/powerpc/platforms/powernv/opal-async.c-269- opal_max_async_tokens);
--
arch/powerpc/platforms/powernv/opal-imc.c=82=static int imc_get_mem_addr_nest(struct device_node *node,
--
arch/powerpc/platforms/powernv/opal-imc.c-110-
arch/powerpc/platforms/powernv/opal-imc.c:111: pmu_ptr->mem_info = kzalloc_objs(*pmu_ptr->mem_info, nr_chips + 1);
arch/powerpc/platforms/powernv/opal-imc.c-112- if (!pmu_ptr->mem_info)
--
arch/powerpc/platforms/powernv/opal-irqchip.c=174=int __init opal_event_init(void)
--
arch/powerpc/platforms/powernv/opal-irqchip.c-223- /* Allocate an IRQ resources array */
arch/powerpc/platforms/powernv/opal-irqchip.c:224: opal_irqs = kzalloc_objs(struct resource, opal_irq_count);
arch/powerpc/platforms/powernv/opal-irqchip.c-225- if (WARN_ON(!opal_irqs)) {
--
arch/powerpc/platforms/powernv/opal-powercap.c=142=void __init opal_powercap_init(void)
--
arch/powerpc/platforms/powernv/opal-powercap.c-152-
arch/powerpc/platforms/powernv/opal-powercap.c:153: pcaps = kzalloc_objs(*pcaps, of_get_child_count(powercap));
arch/powerpc/platforms/powernv/opal-powercap.c-154- if (!pcaps)
--
arch/powerpc/platforms/powernv/opal-powercap.c-183-
arch/powerpc/platforms/powernv/opal-powercap.c:184: pcaps[i].pattrs = kzalloc_objs(struct powercap_attr, j);
arch/powerpc/platforms/powernv/opal-powercap.c-185- if (!pcaps[i].pattrs)
--
arch/powerpc/platforms/powernv/opal-powercap.c-187-
arch/powerpc/platforms/powernv/opal-powercap.c:188: pcaps[i].pg.attrs = kzalloc_objs(struct attribute *, j + 1);
arch/powerpc/platforms/powernv/opal-powercap.c-189- if (!pcaps[i].pg.attrs) {
--
arch/powerpc/platforms/powernv/opal-psr.c=123=void __init opal_psr_init(void)
--
arch/powerpc/platforms/powernv/opal-psr.c-134-
arch/powerpc/platforms/powernv/opal-psr.c:135: psr_attrs = kzalloc_objs(*psr_attrs, of_get_child_count(psr));
arch/powerpc/platforms/powernv/opal-psr.c-136- if (!psr_attrs)
--
arch/powerpc/platforms/powernv/opal-sensor-groups.c=160=void __init opal_sensor_groups_init(void)
--
arch/powerpc/platforms/powernv/opal-sensor-groups.c-170-
arch/powerpc/platforms/powernv/opal-sensor-groups.c:171: sgs = kzalloc_objs(*sgs, of_get_child_count(sg));
arch/powerpc/platforms/powernv/opal-sensor-groups.c-172- if (!sgs)
--
arch/powerpc/platforms/powernv/opal-sensor-groups.c-192-
arch/powerpc/platforms/powernv/opal-sensor-groups.c:193: sgs[i].sgattrs = kzalloc_objs(*sgs[i].sgattrs, nr_attrs);
arch/powerpc/platforms/powernv/opal-sensor-groups.c-194- if (!sgs[i].sgattrs)
--
arch/powerpc/platforms/powernv/opal-sensor-groups.c-196-
arch/powerpc/platforms/powernv/opal-sensor-groups.c:197: sgs[i].sg.attrs = kzalloc_objs(*sgs[i].sg.attrs, nr_attrs + 1);
arch/powerpc/platforms/powernv/opal-sensor-groups.c-198-
--
arch/powerpc/platforms/powernv/opal-sysparam.c=143=void __init opal_sys_param_init(void)
--
arch/powerpc/platforms/powernv/opal-sysparam.c-224-
arch/powerpc/platforms/powernv/opal-sysparam.c:225: attr = kzalloc_objs(*attr, count);
arch/powerpc/platforms/powernv/opal-sysparam.c-226- if (!attr) {
--
arch/powerpc/platforms/pseries/papr_platform_attributes.c=233=static int __init papr_init(void)
--
arch/powerpc/platforms/pseries/papr_platform_attributes.c-297-
arch/powerpc/platforms/pseries/papr_platform_attributes.c:298: papr_groups = kzalloc_objs(*papr_groups, num_attrs);
arch/powerpc/platforms/pseries/papr_platform_attributes.c-299- if (!papr_groups)
--
arch/powerpc/platforms/pseries/papr_platform_attributes.c-315- for (idx = 0; idx < num_attrs; idx++) {
arch/powerpc/platforms/pseries/papr_platform_attributes.c:316: papr_groups[idx].pg.attrs = kzalloc_objs(*papr_groups[idx].pg.attrs,
arch/powerpc/platforms/pseries/papr_platform_attributes.c-317- KOBJ_MAX_ATTRS + 1);
--
arch/powerpc/sysdev/mpic.c=542=static void __init mpic_scan_ht_pics(struct mpic *mpic)
--
arch/powerpc/sysdev/mpic.c-549- /* Allocate fixups array */
arch/powerpc/sysdev/mpic.c:550: mpic->fixups = kzalloc_objs(*mpic->fixups, 128);
arch/powerpc/sysdev/mpic.c-551- BUG_ON(mpic->fixups == NULL);
--
arch/powerpc/sysdev/mpic_msgr.c=167=static int mpic_msgr_probe(struct platform_device *dev)
--
arch/powerpc/sysdev/mpic_msgr.c-190-
arch/powerpc/sysdev/mpic_msgr.c:191: mpic_msgrs = kzalloc_objs(*mpic_msgrs, mpic_msgr_count);
arch/powerpc/sysdev/mpic_msgr.c-192- if (!mpic_msgrs) {
--
arch/powerpc/sysdev/xive/common.c=1118=static int __init xive_init_ipis(void)
--
arch/powerpc/sysdev/xive/common.c-1133-
arch/powerpc/sysdev/xive/common.c:1134: xive_ipis = kzalloc_objs(*xive_ipis, nr_node_ids,
arch/powerpc/sysdev/xive/common.c-1135- GFP_KERNEL | __GFP_NOFAIL);
--
arch/riscv/kernel/tests/kprobes/test-kprobes.c=13=static void test_kprobe_riscv(struct kunit *test)
--
arch/riscv/kernel/tests/kprobes/test-kprobes.c-22-
arch/riscv/kernel/tests/kprobes/test-kprobes.c:23: kp = kzalloc_objs(*kp, num_kprobe);
arch/riscv/kernel/tests/kprobes/test-kprobes.c-24- KUNIT_EXPECT_TRUE(test, kp);
--
arch/riscv/kernel/unaligned_access_speed.c=154=static void __init check_unaligned_access_speed_all_cpus(void)
--
arch/riscv/kernel/unaligned_access_speed.c-157- unsigned int cpu_count = num_possible_cpus();
arch/riscv/kernel/unaligned_access_speed.c:158: struct page **bufs = kzalloc_objs(*bufs, cpu_count);
arch/riscv/kernel/unaligned_access_speed.c-159-
--
arch/riscv/kernel/vdso.c=44=static void __init __vdso_init(struct __vdso_info *vdso_info)
--
arch/riscv/kernel/vdso.c-57-
arch/riscv/kernel/vdso.c:58: vdso_pagelist = kzalloc_objs(struct page *, vdso_info->vdso_pages);
arch/riscv/kernel/vdso.c-59- if (vdso_pagelist == NULL)
--
arch/riscv/kvm/aia_aplic.c=574=int kvm_riscv_aia_aplic_init(struct kvm *kvm)
--
arch/riscv/kvm/aia_aplic.c-591- aplic->nr_words = DIV_ROUND_UP(aplic->nr_irqs, 32);
arch/riscv/kvm/aia_aplic.c:592: aplic->irqs = kzalloc_objs(*aplic->irqs, aplic->nr_irqs);
arch/riscv/kvm/aia_aplic.c-593- if (!aplic->irqs) {
--
arch/riscv/kvm/vcpu_sbi_fwft.c=348=static int kvm_sbi_ext_fwft_init(struct kvm_vcpu *vcpu)
--
arch/riscv/kvm/vcpu_sbi_fwft.c-354-
arch/riscv/kvm/vcpu_sbi_fwft.c:355: fwft->configs = kzalloc_objs(struct kvm_sbi_fwft_config,
arch/riscv/kvm/vcpu_sbi_fwft.c-356- ARRAY_SIZE(features));
--
arch/riscv/kvm/vm.c=93=int kvm_riscv_setup_default_irq_routing(struct kvm *kvm, u32 lines)
--
arch/riscv/kvm/vm.c-97-
arch/riscv/kvm/vm.c:98: ents = kzalloc_objs(*ents, lines);
arch/riscv/kvm/vm.c-99- if (!ents)
--
arch/riscv/net/bpf_jit_comp64.c=1022=static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
--
arch/riscv/net/bpf_jit_comp64.c-1197- if (fmod_ret->nr_links) {
arch/riscv/net/bpf_jit_comp64.c:1198: branches_off = kzalloc_objs(int, fmod_ret->nr_links);
arch/riscv/net/bpf_jit_comp64.c-1199- if (!branches_off)
--
arch/riscv/net/bpf_jit_core.c=44=struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
--
arch/riscv/net/bpf_jit_core.c-74- ctx->prog = prog;
arch/riscv/net/bpf_jit_core.c:75: ctx->offset = kzalloc_objs(int, prog->len);
arch/riscv/net/bpf_jit_core.c-76- if (!ctx->offset)
--
arch/s390/appldata/appldata_base.c=348=int appldata_register_ops(struct appldata_ops *ops)
--
arch/s390/appldata/appldata_base.c-352-
arch/s390/appldata/appldata_base.c:353: ops->ctl_table = kzalloc_objs(struct ctl_table, 1);
arch/s390/appldata/appldata_base.c-354- if (!ops->ctl_table)
--
arch/s390/include/asm/idals.h=88=static inline int set_normalized_cda(struct ccw1 *ccw, void *vaddr)
--
arch/s390/include/asm/idals.h-96- if (nridaws > 0) {
arch/s390/include/asm/idals.h:97: idal = kzalloc_objs(*idal, nridaws, GFP_ATOMIC | GFP_DMA);
arch/s390/include/asm/idals.h-98- if (!idal)
--
arch/s390/kernel/cert_store.c=315=static int invalidate_keyring_keys(struct key *keyring)
--
arch/s390/kernel/cert_store.c-324- num_keys = keyring_payload_len / sizeof(key_serial_t);
arch/s390/kernel/cert_store.c:325: key_array = kzalloc_objs(key_serial_t, num_keys);
arch/s390/kernel/cert_store.c-326- if (!key_array)
--
arch/s390/kernel/debug.c=220=static debug_info_t *debug_info_alloc(const char *name, int pages_per_area,
--
arch/s390/kernel/debug.c-229- goto fail_malloc_rc;
arch/s390/kernel/debug.c:230: rc->active_entries = kzalloc_objs(int, nr_areas);
arch/s390/kernel/debug.c-231- if (!rc->active_entries)
arch/s390/kernel/debug.c-232- goto fail_malloc_active_entries;
arch/s390/kernel/debug.c:233: rc->active_pages = kzalloc_objs(int, nr_areas);
arch/s390/kernel/debug.c-234- if (!rc->active_pages)
--
arch/s390/kernel/vdso.c=129=static struct page ** __init vdso_setup_pages(void *start, void *end)
--
arch/s390/kernel/vdso.c-134-
arch/s390/kernel/vdso.c:135: pagelist = kzalloc_objs(struct page *, pages + 1);
arch/s390/kernel/vdso.c-136- if (!pagelist)
--
arch/s390/kvm/pci.c=120=int kvm_s390_pci_aen_init(u8 nisc)
--
arch/s390/kvm/pci.c-128- mutex_lock(&aift->aift_lock);
arch/s390/kvm/pci.c:129: aift->kzdev = kzalloc_objs(struct kvm_zdev *, ZPCI_NR_DEVICES);
arch/s390/kvm/pci.c-130- if (!aift->kzdev) {
--
arch/s390/pci/pci.c=1065=static int zpci_mem_init(void)
--
arch/s390/pci/pci.c-1076-
arch/s390/pci/pci.c:1077: zpci_iomap_start = kzalloc_objs(*zpci_iomap_start, ZPCI_IOMAP_ENTRIES);
arch/s390/pci/pci.c-1078- if (!zpci_iomap_start)
--
arch/s390/pci/pci_irq.c=552=static int __init zpci_directed_irq_init(void)
--
arch/s390/pci/pci_irq.c-565-
arch/s390/pci/pci_irq.c:566: zpci_ibv = kzalloc_objs(*zpci_ibv, num_possible_cpus());
arch/s390/pci/pci_irq.c-567- if (!zpci_ibv)
--
arch/s390/pci/pci_irq.c=590=static int __init zpci_floating_irq_init(void)
arch/s390/pci/pci_irq.c-591-{
arch/s390/pci/pci_irq.c:592: zpci_ibv = kzalloc_objs(*zpci_ibv, ZPCI_NR_DEVICES);
arch/s390/pci/pci_irq.c-593- if (!zpci_ibv)
--
arch/sh/drivers/dma/dmabrg.c=151=static int __init dmabrg_init(void)
--
arch/sh/drivers/dma/dmabrg.c-155-
arch/sh/drivers/dma/dmabrg.c:156: dmabrg_handlers = kzalloc_objs(struct dmabrg_handler, 10);
arch/sh/drivers/dma/dmabrg.c-157- if (!dmabrg_handlers)
--
arch/sh/drivers/pci/pcie-sh7786.c=545=static int __init sh7786_pcie_init(void)
--
arch/sh/drivers/pci/pcie-sh7786.c-560-
arch/sh/drivers/pci/pcie-sh7786.c:561: sh7786_pcie_ports = kzalloc_objs(struct sh7786_pcie_port, nr_ports);
arch/sh/drivers/pci/pcie-sh7786.c-562- if (unlikely(!sh7786_pcie_ports))
--
arch/sparc/kernel/sys_sparc_64.c=625=SYSCALL_DEFINE5(utrap_install, utrap_entry_t, type,
--
arch/sparc/kernel/sys_sparc_64.c-649- current_thread_info()->utraps =
arch/sparc/kernel/sys_sparc_64.c:650: kzalloc_objs(long, UT_TRAP_INSTRUCTION_31 + 1);
arch/sparc/kernel/sys_sparc_64.c-651- if (!current_thread_info()->utraps)
--
arch/sparc/vdso/vma.c=46=static int __init init_vdso_image(const struct vdso_image *image,
--
arch/sparc/vdso/vma.c-60-
arch/sparc/vdso/vma.c:61: cpp = kzalloc_objs(struct page *, cnpages);
arch/sparc/vdso/vma.c-62- vdso_mapping->pages = cpp;
--
arch/x86/events/amd/iommu.c=382=static __init int _init_events_attrs(void)
--
arch/x86/events/amd/iommu.c-389-
arch/x86/events/amd/iommu.c:390: attrs = kzalloc_objs(*attrs, i + 1);
arch/x86/events/amd/iommu.c-391- if (!attrs)
--
arch/x86/events/intel/core.c=7420=static __always_inline int intel_pmu_init_hybrid(enum hybrid_pmu_type pmus)
--
arch/x86/events/intel/core.c-7426- x86_pmu.num_hybrid_pmus = hweight_long(pmus_mask);
arch/x86/events/intel/core.c:7427: x86_pmu.hybrid_pmu = kzalloc_objs(struct x86_hybrid_pmu,
arch/x86/events/intel/core.c-7428- x86_pmu.num_hybrid_pmus);
--
arch/x86/events/intel/uncore.c=988=static int __init uncore_type_init(struct intel_uncore_type *type)
--
arch/x86/events/intel/uncore.c-993-
]
|
| 26/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 83677
output: 40
thoughts: 401
Thoughts: **Analyzing the Thread Allocation in NFSD**
Okay, so I'm looking at this code and immediately see `kzalloc_objs`. Right, that's just a macro or inline function that does a `kcalloc`, allocating memory for an array. Specifically, `kcalloc(count, sizeof(type), GFP_KERNEL)`. Got it. Now, it's used to allocate `nthreads = kzalloc_objs(int, nrpools);`.
Wait a second, `kzalloc_objs` isn't a standard function. I have to find where that's defined, it must be in a header somewhere. Let me make a mental note to check where it's defined. And it looks like `kzalloc_objs(int, nrpools)` is effectively allocating `nrpools * sizeof(int)` bytes.
Now I'm thinking about edge cases, if `nrpools` is *very* large, that allocation could potentially block for a long time or even fail due to memory constraints. But, `nrpools` appears to come from the number of `NFSD_A_SERVER_THREADS` attributes in the netlink message.
Ah, right, `nlmsg_for_each_attr_type` is iterating through the attributes in the netlink message, and that's how `nrpools` is populated. So, if the message has a huge number of attributes, then `nrpools` could be substantial.
Hang on though, the netlink message size is limited, isn't it? Something like 32KB? Yes, it is! So, `nrpools` should be constrained to a reasonable size, maybe a few thousand at most. That's good. In reality, `kzalloc_objs` is just another way to say `kcalloc(nrpools, sizeof(int), GFP_KERNEL)`.
Okay, digging further into `nfsd_nl_threads_set_doit`, I see that it does the following:
```c
mutex_lock(&nfsd_mutex);
nthreads = kzalloc_objs(int, nrpools);
if (!nthreads) {
ret = -ENOMEM;
goto out_unlock;
}
```
Wait, it takes a mutex *before* the allocation, that makes sense for synchronization. It allocates with `kzalloc_objs` (which we've already discussed), and then checks for an allocation failure, returning `-ENOMEM` if it fails. Then, later, it does `ret = nfsd_svc(nrpools, nthreads, net, current_cred(), scope);`.
Okay, I need to understand what `nfsd_svc` actually *does* here. Let me check that next, to see how the allocated `nthreads` is used.
|
| 27/2 |
2026/06/01 06:17 |
tool |
read-file |
0m
Args: map[File:fs/nfsd/nfssvc.c FirstLine:751 LineCount:100]
Results: map[Contents: 751: nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
752: {
753: int error;
754: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
755: struct svc_serv *serv;
756:
757: lockdep_assert_held(&nfsd_mutex);
758:
759: dprintk("nfsd: creating service\n");
760:
761: strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename,
762: sizeof(nn->nfsd_name));
763:
764: error = nfsd_create_serv(net);
765: if (error)
766: goto out;
767: serv = nn->nfsd_serv;
768:
769: error = nfsd_startup_net(net, cred);
770: if (error)
771: goto out_put;
772: error = nfsd_set_nrthreads(n, nthreads, net);
773: if (error)
774: goto out_put;
775: error = serv->sv_nrthreads;
776: out_put:
777: if (serv->sv_nrthreads == 0)
778: nfsd_destroy_serv(net);
779: out:
780: return error;
781: }
782:
783: #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
784: static bool
785: nfsd_support_acl_version(int vers)
786: {
787: if (vers >= NFSD_ACL_MINVERS && vers < NFSD_ACL_NRVERS)
788: return nfsd_acl_version[vers] != NULL;
789: return false;
790: }
791:
792: static int
793: nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp,
794: u32 version, int family, unsigned short proto,
795: unsigned short port)
796: {
797: if (!nfsd_support_acl_version(version) ||
798: !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
799: return 0;
800: return svc_generic_rpcbind_set(net, progp, version, family,
801: proto, port);
802: }
803:
804: static __be32
805: nfsd_acl_init_request(struct svc_rqst *rqstp,
806: const struct svc_program *progp,
807: struct svc_process_info *ret)
808: {
809: struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
810: int i;
811:
812: if (likely(nfsd_support_acl_version(rqstp->rq_vers) &&
813: nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
814: return svc_generic_init_request(rqstp, progp, ret);
815:
816: ret->mismatch.lovers = NFSD_ACL_NRVERS;
817: for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) {
818: if (nfsd_support_acl_version(rqstp->rq_vers) &&
819: nfsd_vers(nn, i, NFSD_TEST)) {
820: ret->mismatch.lovers = i;
821: break;
822: }
823: }
824: if (ret->mismatch.lovers == NFSD_ACL_NRVERS)
825: return rpc_prog_unavail;
826: ret->mismatch.hivers = NFSD_ACL_MINVERS;
827: for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
828: if (nfsd_support_acl_version(rqstp->rq_vers) &&
829: nfsd_vers(nn, i, NFSD_TEST)) {
830: ret->mismatch.hivers = i;
831: break;
832: }
833: }
834: return rpc_prog_mismatch;
835: }
836: #endif
837:
838: static int
839: nfsd_rpcbind_set(struct net *net, const struct svc_program *progp,
840: u32 version, int family, unsigned short proto,
841: unsigned short port)
842: {
843: if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
844: return 0;
845: return svc_generic_rpcbind_set(net, progp, version, family,
846: proto, port);
847: }
848:
849: static __be32
850: nfsd_init_request(struct svc_rqst *rqstp,
]
|
| 28/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 86360
output: 40
thoughts: 58
Thoughts: Here's my summary, as the thought process unfolds:
**Digging into the NFS Daemon Initialization**
Okay, so I'm tracing through the NFS server initialization sequence, and I've hit a key point: `nfsd_svc` is calling both `nfsd_startup_net(net, cred);` *and* `nfsd_set_nrthreads(n, nthreads, net);`. Interesting. It's good to see the net context is passed to both. Seems logical. I'm especially interested in understanding how threads are managed in the NFS server. The `nfsd_set_nrthreads` call piques my curiosity. I need to understand what this function is actually doing with `nthreads`. Let's dive into the code for `nfsd_set_nrthreads` and see what's happening.
|
| 29/2 |
2026/06/01 06:17 |
tool |
read-file |
0m
Args: map[File:fs/nfsd/nfssvc.c FirstLine:680 LineCount:100]
Results: map[Contents: 680: int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
681: {
682: int i = 0;
683: int tot = 0;
684: int err = 0;
685: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
686:
687: lockdep_assert_held(&nfsd_mutex);
688:
689: if (nn->nfsd_serv == NULL || n <= 0)
690: return 0;
691:
692: /* Special case: When n == 1, distribute threads equally among pools. */
693: if (n == 1)
694: return svc_set_num_threads(nn->nfsd_serv, nn->min_threads, nthreads[0]);
695:
696: if (n > nn->nfsd_serv->sv_nrpools)
697: n = nn->nfsd_serv->sv_nrpools;
698:
699: /* enforce a global maximum number of threads */
700: tot = 0;
701: for (i = 0; i < n; i++) {
702: nthreads[i] = min(nthreads[i], NFSD_MAXSERVS);
703: tot += nthreads[i];
704: }
705: if (tot > NFSD_MAXSERVS) {
706: /* total too large: scale down requested numbers */
707: for (i = 0; i < n && tot > 0; i++) {
708: int new = nthreads[i] * NFSD_MAXSERVS / tot;
709: tot -= (nthreads[i] - new);
710: nthreads[i] = new;
711: }
712: for (i = 0; i < n && tot > 0; i++) {
713: nthreads[i]--;
714: tot--;
715: }
716: }
717:
718: /* apply the new numbers */
719: for (i = 0; i < n; i++) {
720: err = svc_set_pool_threads(nn->nfsd_serv,
721: &nn->nfsd_serv->sv_pools[i],
722: nn->min_threads, nthreads[i]);
723: if (err)
724: goto out;
725: }
726:
727: /* Anything undefined in array is considered to be 0 */
728: for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) {
729: err = svc_set_pool_threads(nn->nfsd_serv,
730: &nn->nfsd_serv->sv_pools[i],
731: 0, 0);
732: if (err)
733: goto out;
734: }
735: out:
736: return err;
737: }
738:
739: /**
740: * nfsd_svc: start up or shut down the nfsd server
741: * @n: number of array members in @nthreads
742: * @nthreads: array of thread counts for each pool
743: * @net: network namespace to operate within
744: * @cred: credentials to use for xprt creation
745: * @scope: server scope value (defaults to nodename)
746: *
747: * Adjust the number of threads in each pool and return the new
748: * total number of threads in the service.
749: */
750: int
751: nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
752: {
753: int error;
754: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
755: struct svc_serv *serv;
756:
757: lockdep_assert_held(&nfsd_mutex);
758:
759: dprintk("nfsd: creating service\n");
760:
761: strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename,
762: sizeof(nn->nfsd_name));
763:
764: error = nfsd_create_serv(net);
765: if (error)
766: goto out;
767: serv = nn->nfsd_serv;
768:
769: error = nfsd_startup_net(net, cred);
770: if (error)
771: goto out_put;
772: error = nfsd_set_nrthreads(n, nthreads, net);
773: if (error)
774: goto out_put;
775: error = serv->sv_nrthreads;
776: out_put:
777: if (serv->sv_nrthreads == 0)
778: nfsd_destroy_serv(net);
779: out:
]
|
| 30/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 88206
output: 21
thoughts: 46
Thoughts: Okay, let's see...
**Analyzing NFS Daemon Threading**
Alright, I need to understand this threading setup for the NFS daemon. Let me see... if I'm reading this correctly, `nfsd_set_nrthreads` calls either `svc_set_num_threads` or `svc_set_pool_threads`. The logic behind that choice is something I'll need to dig into later, but the important bit for now is focusing on what happens inside `svc_set_num_threads`. Let's take a closer look at that function, then we can figure out the implications.
|
| 31/2 |
2026/06/01 06:17 |
tool |
grepper |
0m
Args: map[Expression:svc_set_num_threads]
Results: map[Output:fs/lockd/svc.c=318=static int lockd_get(void)
--
fs/lockd/svc.c-350-
fs/lockd/svc.c:351: error = svc_set_num_threads(serv, 0, 1);
fs/lockd/svc.c-352- if (error < 0) {
--
fs/lockd/svc.c=367=static void lockd_put(void)
--
fs/lockd/svc.c-378-
fs/lockd/svc.c:379: svc_set_num_threads(nlmsvc_serv, 0, 0);
fs/lockd/svc.c-380- timer_delete_sync(&nlmsvc_retry);
--
fs/nfs/callback.c=101=static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
--
fs/nfs/callback.c-114-
fs/nfs/callback.c:115: ret = svc_set_num_threads(serv, 0, nrservs);
fs/nfs/callback.c-116- if (ret) {
fs/nfs/callback.c:117: svc_set_num_threads(serv, 0, 0);
fs/nfs/callback.c-118- return ret;
--
fs/nfs/callback.c=208=int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
--
fs/nfs/callback.c-233- if (!cb_info->users) {
fs/nfs/callback.c:234: svc_set_num_threads(cb_info->serv, 0, 0);
fs/nfs/callback.c-235- svc_destroy(&cb_info->serv);
--
fs/nfs/callback.c=250=void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
--
fs/nfs/callback.c-259- if (cb_info->users == 0) {
fs/nfs/callback.c:260: svc_set_num_threads(serv, 0, 0);
fs/nfs/callback.c-261- dprintk("nfs_callback_down: service destroyed\n");
--
fs/nfsd/nfssvc.c=571=void nfsd_shutdown_threads(struct net *net)
--
fs/nfsd/nfssvc.c-583- /* Kill outstanding nfsd threads */
fs/nfsd/nfssvc.c:584: svc_set_num_threads(serv, 0, 0);
fs/nfsd/nfssvc.c-585- nfsd_destroy_serv(net);
--
fs/nfsd/nfssvc.c=680=int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
--
fs/nfsd/nfssvc.c-693- if (n == 1)
fs/nfsd/nfssvc.c:694: return svc_set_num_threads(nn->nfsd_serv, nn->min_threads, nthreads[0]);
fs/nfsd/nfssvc.c-695-
--
include/linux/sunrpc/svc.h=468=int svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
include/linux/sunrpc/svc.h-469- unsigned int min_threads, unsigned int max_threads);
include/linux/sunrpc/svc.h:470:int svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
include/linux/sunrpc/svc.h-471- unsigned int nrservs);
--
net/sunrpc/svc.c=701=svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
--
net/sunrpc/svc.c-740- /* Protected by whatever lock the service uses when calling
net/sunrpc/svc.c:741: * svc_set_num_threads()
net/sunrpc/svc.c-742- */
--
net/sunrpc/svc.c=913=EXPORT_SYMBOL_GPL(svc_set_pool_threads);
--
net/sunrpc/svc.c-915-/**
net/sunrpc/svc.c:916: * svc_set_num_threads - adjust number of threads in serv
net/sunrpc/svc.c-917- * @serv: RPC service to adjust
--
net/sunrpc/svc.c=932=int
net/sunrpc/svc.c:933:svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
net/sunrpc/svc.c-934- unsigned int nrservs)
--
net/sunrpc/svc.c-954-}
net/sunrpc/svc.c:955:EXPORT_SYMBOL_GPL(svc_set_num_threads);
net/sunrpc/svc.c-956-
--
net/sunrpc/svc.c=996=void svc_rqst_release_pages(struct svc_rqst *rqstp)
--
net/sunrpc/svc.c-1018- * service mutex held. Normally this is held by a DIFFERENT thread, the
net/sunrpc/svc.c:1019: * one that is calling svc_set_num_threads() and which will wait for
net/sunrpc/svc.c-1020- * SP_VICTIM_REMAINS to be cleared before dropping the mutex. If the
net/sunrpc/svc.c-1021- * thread exits for any reason other than svc_thread_should_stop()
net/sunrpc/svc.c:1022: * returning %true (which indicated that svc_set_num_threads() is
net/sunrpc/svc.c-1023- * waiting for it to exit), then it must take the service mutex itself,
]
|
| 32/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 89998
output: 36
thoughts: 24
Thoughts: Here's my take on the `svc_set_num_threads` function, as if I'm walking through it myself:
**Diving into `svc_set_num_threads` in `net/sunrpc/svc.c`**
Okay, time to dig into `svc_set_num_threads` – that function that's supposed to handle thread scaling within the SunRPC service layer. Let's see what it's really doing under the hood, I'm already anticipating some of the nuances around thread pools and concurrency that likely arise when tuning this kind of thing.
First, I need to understand the goals. Presumably, this function is responsible for dynamically adjusting the number of worker threads to optimize performance or handle varying load conditions, right? I'd imagine that it's designed to avoid unnecessary overhead when the system is idle while also preventing bottlenecks under heavy load. Let's see if the code reflects this intuition.
I'll be looking for things like:
* **Synchronization Mechanisms:** Are they using mutexes, semaphores, or other techniques to protect shared resources, especially data related to the service's state, thread pools, and any work queues? I hope it's well-managed and avoids potential race conditions.
* **Thread Creation/Destruction:** How does it create and terminate threads? Is it a static pool, or does it dynamically adjust the number based on the current load? I'm hoping it avoids frequent thread creation/destruction, as that can be expensive.
* **Load Balancing:** Is there any mechanism to distribute requests among the threads? Does it use some kind of scheduling algorithm or is it simply a FIFO queue for distributing tasks? I would expect a good balance of new vs current threads in the pool for maximum throughput.
* **Concurrency Limits:** Does it impose any limits on the number of threads to avoid resource exhaustion? A safeguard for runaway scaling is crucial, as too many threads could cause more harm than good.
* **Error Handling:** What happens if thread creation fails? Is there any graceful degradation or retry mechanism? Robust error handling is important to prevent the service from crashing or becoming unresponsive.
* **Interaction with RPC Requests:** How does it tie into the RPC request handling mechanism? Does it update some per-request data? Does it schedule some new work?
I'll be paying close attention to any potential bottlenecks or areas where performance could be improved. I'm expecting some kind of threshold or load-based trigger for the thread adjustments. I hope the design is efficient, scalable, and doesn't introduce any new vulnerabilities. Time to start reading!
|
| 33/2 |
2026/06/01 06:17 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc.c FirstLine:932 LineCount:50]
Results: map[Contents: 932: int
933: svc_set_num_threads(struct svc_serv *serv, unsigned int min_threads,
934: unsigned int nrservs)
935: {
936: unsigned int base = nrservs / serv->sv_nrpools;
937: unsigned int remain = nrservs % serv->sv_nrpools;
938: int i, err = 0;
939:
940: for (i = 0; i < serv->sv_nrpools; ++i) {
941: struct svc_pool *pool = &serv->sv_pools[i];
942: int threads = base;
943:
944: if (remain) {
945: ++threads;
946: --remain;
947: }
948:
949: err = svc_set_pool_threads(serv, pool, min_threads, threads);
950: if (err)
951: break;
952: }
953: return err;
954: }
955: EXPORT_SYMBOL_GPL(svc_set_num_threads);
956:
957: /**
958: * svc_rqst_replace_page - Replace one page in rq_respages[]
959: * @rqstp: svc_rqst with pages to replace
960: * @page: replacement page
961: *
962: * When replacing a page in rq_respages, batch the release of the
963: * replaced pages to avoid hammering the page allocator.
964: *
965: * Return values:
966: * %true: page replaced
967: * %false: array bounds checking failed
968: */
969: bool svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page)
970: {
971: struct page **begin = rqstp->rq_respages;
972: struct page **end = rqstp->rq_page_end;
973:
974: if (unlikely(rqstp->rq_next_page < begin || rqstp->rq_next_page > end)) {
975: trace_svc_replace_page_err(rqstp);
976: return false;
977: }
978:
979: if (*rqstp->rq_next_page)
980: svc_rqst_page_release(rqstp, *rqstp->rq_next_page);
981:
]
|
| 34/2 |
2026/06/01 06:17 |
llm |
expert |
0m
Model:
Tokens:
input: 91358
output: 36
thoughts: 24
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `svc_set_pool_threads`**
Alright, let's get into the details of thread pool management in `net/sunrpc/svc.c`, specifically focusing on `svc_set_pool_threads`. I'm guessing this function is central to how the RPC server handles concurrency and resource allocation. I want to see how it's setting the number of threads and how it interacts with the underlying kernel infrastructure. I expect to find details about thread creation, destruction, perhaps thread affinity settings, and potentially some heuristics or configurations for dynamic scaling. I'll need to pay close attention to any locking mechanisms used to protect shared data structures related to the thread pool, as concurrency bugs are common in multithreaded server applications. I also want to understand the implications of the thread count on performance and resource consumption. Is it a fixed value? Is there any interaction with the system load or administrator configured options? My goal is to grasp the core mechanics of the thread pool's behavior, and potentially identify areas for optimization or potential issues, especially related to the handling of a variety of RPC requests.
|
| 35/2 |
2026/06/01 06:18 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc.c FirstLine:865 LineCount:50]
Results: map[Contents: 865: *
866: * If @min_threads is 0 or larger than @max_threads, then it is ignored and
867: * the pool will be set to run a static @max_threads number of threads.
868: *
869: * Caller must ensure mutual exclusion between this and server startup or
870: * shutdown.
871: *
872: * Returns zero on success or a negative errno if an error occurred while
873: * starting a thread.
874: */
875: int
876: svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
877: unsigned int min_threads, unsigned int max_threads)
878: {
879: int delta;
880:
881: if (!pool)
882: return -EINVAL;
883:
884: /* clamp min threads to the max */
885: if (min_threads > max_threads)
886: min_threads = max_threads;
887:
888: pool->sp_nrthrmin = min_threads;
889: pool->sp_nrthrmax = max_threads;
890:
891: /*
892: * When min_threads is set, then only change the number of
893: * threads to bring it within an acceptable range.
894: */
895: if (min_threads) {
896: if (pool->sp_nrthreads > max_threads)
897: delta = max_threads;
898: else if (pool->sp_nrthreads < min_threads)
899: delta = min_threads;
900: else
901: return 0;
902: } else {
903: delta = max_threads;
904: }
905:
906: delta -= pool->sp_nrthreads;
907: if (delta > 0)
908: return svc_start_kthreads(serv, pool, delta);
909: if (delta < 0)
910: return svc_stop_kthreads(serv, pool, delta);
911: return 0;
912: }
913: EXPORT_SYMBOL_GPL(svc_set_pool_threads);
914:
]
|
| 36/2 |
2026/06/01 06:18 |
llm |
expert |
0m
Model:
Tokens:
input: 92367
output: 36
thoughts: 32
Thoughts: Here's my analysis of the provided task, assuming I'm an expert:
**Diving into the RPC Kernel Threads**
Okay, so the goal is to get a handle on how the kernel-level threads used by the Sun RPC service are started and stopped. That means cracking open `net/sunrpc/svc.c` and specifically focusing on the `svc_start_kthreads` and `svc_stop_kthreads` functions. I'm already picturing the likely scenarios: thread creation within `svc_start_kthreads`, probably using `kthread_create` or a similar kernel primitive, and then management of these threads including signal handling or a cleanup routine in `svc_stop_kthreads`. There'll be considerations for error handling, graceful shutdowns, and probably some synchronization primitives like mutexes or semaphores to ensure the threads interact safely with the core RPC data structures. I'll need to pay close attention to how resources are allocated and freed, keeping an eye out for potential memory leaks or race conditions. This should give me a good understanding of the lifecycle and internal workings of these threads, and hopefully reveal any performance bottlenecks or potential areas for optimization.
|
| 37/2 |
2026/06/01 06:18 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc.c FirstLine:810 LineCount:50]
Results: map[Contents: 810: if (IS_ERR(task)) {
811: err = PTR_ERR(task);
812: goto out;
813: }
814:
815: rqstp->rq_task = task;
816: if (serv->sv_nrpools > 1)
817: svc_pool_map_set_cpumask(task, pool->sp_id);
818:
819: svc_sock_update_bufs(serv);
820: wake_up_process(task);
821:
822: /* Wait for the thread to signal initialization status */
823: wait_var_event(&rqstp->rq_err, rqstp->rq_err != -EAGAIN);
824: err = rqstp->rq_err;
825: out:
826: if (err)
827: svc_exit_thread(rqstp);
828: return err;
829: }
830: EXPORT_SYMBOL_GPL(svc_new_thread);
831:
832: static int
833: svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
834: {
835: int err = 0;
836:
837: while (!err && nrservs--)
838: err = svc_new_thread(serv, pool);
839:
840: return err;
841: }
842:
843: static int
844: svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
845: {
846: do {
847: set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
848: set_bit(SP_NEED_VICTIM, &pool->sp_flags);
849: svc_pool_wake_idle_thread(pool);
850: wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE);
851: nrservs++;
852: } while (nrservs < 0);
853: return 0;
854: }
855:
856: /**
857: * svc_set_pool_threads - adjust number of threads per pool
858: * @serv: RPC service to adjust
859: * @pool: Specific pool from which to choose threads
]
|
| 38/2 |
2026/06/01 06:18 |
llm |
expert |
0m
Model:
Tokens:
input: 93423
output: 26
thoughts: 2984
Thoughts: **Analyzing a Potential NFSd Kernel Hang**
Okay, here's the situation I've got: I'm staring at a potential hang in the NFSd code, specifically with `svc_stop_kthreads`. It looks like it's waiting on `SP_VICTIM_REMAINS` to clear, but if something goes wrong, it's easy to get stuck there with the `TASK_IDLE` state in `wait_on_bit`, which is uninterruptible. That wait should show up in lockdep if it's blocking the task.
I see from lockdep that there are a bunch of `syz.*` tasks, and they're all blocked in `__mutex_lock` within `nfsd_nl_threads_set_doit`. That's where it's interesting because the lockdep output seems to indicate the `nfsd_mutex` is *held* by one of them at `nfsd_nl_threads_set_doit+0x8ec/0x12b0`. But wait, they're *blocked* on it! That makes no sense; the blocked task shouldn't be showing as holding the mutex. Lockdep shows the state *before* the sleep so the block would be on the mutex.
It's clear that if all the syzkaller tasks are blocked there, then none of them actually has the mutex because `mutex_lock` acquires the lock *before* sleeping. So, either the mutex isn't locked, or it's held by a task NOT in the lockdep output.
I'm thinking, could it be a task that exited? Or maybe lockdep is truncated? Nope, the output doesn't seem to be truncated. I also checked whether a kernel thread, like `nfsd`, holds it and it's trying to release it, but it appears to be using a `mutex_trylock` and quickly releasing the mutex, if it can.
Could the holding task be blocked in `svc_stop_kthreads`? This would be reflected in the lockdep output.
I checked `wait_on_bit` and if the task is in `TASK_IDLE` state it would show up. If it's running it would show up.
Alright, something isn't right here. I'm focusing on that address within `nfsd_nl_threads_set_doit+0x8ec/0x12b0`. I need to figure out what that code is doing. I see the function is not that big, and the provided offset seems large. Is that an inlined function? Unless LTO is enabled, which isn't common for these kernels, inlining isn't the problem. The address is close to the start of the `wait_on_bit` function. It's likely `nfsd_svc` -> `nfsd_set_nrthreads` -> `svc_set_pool_threads` -> `svc_stop_kthreads` -> `wait_on_bit`.
|
| 39/2 |
2026/06/01 06:18 |
tool |
grepper |
0m
Args: map[Expression:nfsd_nl_threads_set_doit]
Results: map[Output:fs/nfsd/netlink.c=52=static const struct genl_split_ops nfsd_nl_ops[] = {
--
fs/nfsd/netlink.c-59- .cmd = NFSD_CMD_THREADS_SET,
fs/nfsd/netlink.c:60: .doit = nfsd_nl_threads_set_doit,
fs/nfsd/netlink.c-61- .policy = nfsd_threads_set_nl_policy,
--
fs/nfsd/netlink.h=19=int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
fs/nfsd/netlink.h-20- struct netlink_callback *cb);
fs/nfsd/netlink.h:21:int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info);
fs/nfsd/netlink.h-22-int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info);
--
fs/nfsd/nfsctl.c=1594=static int nfsd_nl_fh_key_set(const struct nlattr *attr, struct nfsd_net *nn)
--
fs/nfsd/nfsctl.c-1610-/**
fs/nfsd/nfsctl.c:1611: * nfsd_nl_threads_set_doit - set the number of running threads
fs/nfsd/nfsctl.c-1612- * @skb: reply buffer
--
fs/nfsd/nfsctl.c-1616- */
fs/nfsd/nfsctl.c:1617:int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
fs/nfsd/nfsctl.c-1618-{
]
|
| 40/2 |
2026/06/01 06:18 |
llm |
expert |
0m
Model:
Tokens:
input: 97612
output: 25
thoughts: 1314
Thoughts: **Analyzing a Potential NFSd Mutex Contention Issue**
Okay, let's break this down. My initial observation is that `nfsd_nl_threads_set_doit` is in `fs/nfsd/nfsctl.c`. The fact that `mutex_lock` seems to be at offset `0x8ec` suggests a large function, which is not unusual given the presence of KASAN, which can bloat function sizes considerably. So, `nfsd_nl_threads_set_doit+0x8ec/0x12b0` *is* likely the `mutex_lock(&nfsd_mutex)` call.
The core problem is that 30 tasks are all seemingly blocked at this specific mutex. If they're *all* blocked at the mutex acquisition, logically *none* of them should hold it. If nobody holds it, why are they blocked? A standard mutex, if uncontended, should simply be acquired and return immediately. So, the fact that they are blocked means it *must* be held by someone. The immediate questions become: Who holds the mutex, and why isn't it releasing?
The first thing I looked at was the `syz-executor/7333` task, but it doesn't appear to hold `nfsd_mutex` based on the lockdep output. It does have several other locks, but not the one we're interested in. Could it have *previously* held the mutex and then done an `execve`? No, locks aren't released on `execve`, at least not in a way that would leave the mutex *locked*.
What about a task exiting with the mutex held? Lockdep *should* have alerted us to this, which it didn't. The key here is, is there any other way that the lock could be held without being obvious in the lockdep output?
Ah, wait. Could the task *holding* the mutex actually be *one of the tasks that's blocked*? Let's check the lockdep output for one of the other tasks. Specifically, let's look at `syz.2.19/5987`:
Bingo! It appears `syz.2.19/5987` *does* hold `nfsd_mutex`! Moreover, it's blocked. So, if `syz.2.19/5987` *is* holding `nfsd_mutex`, and it's also blocked, that's the deadlock. This is the smoking gun!
But, upon further review, I can see from the call stack that `syz.2.19/5987` is blocked in `__mutex_lock_common`. This is the *acquiring* function! Therefore, it is *trying* to acquire `nfsd_mutex`, it is *not* holding it.
Okay, so *none* of the blocked 30 tasks holds `nfsd_mutex`. This brings us back to square one: who holds the mutex? Let's search the kernel source for all instances of `mutex_lock(&nfsd_mutex)` to see what else is going on.
|
| 41/2 |
2026/06/01 06:18 |
tool |
grepper |
0m
Args: map[Expression:mutex_lock\(&nfsd_mutex\)]
Results: map[Output:fs/nfsd/export.c=255=static void expkey_flush(void)
--
fs/nfsd/export.c-260- */
fs/nfsd/export.c:261: mutex_lock(&nfsd_mutex);
fs/nfsd/export.c-262- nfsd_file_cache_purge(current->nsproxy->net_ns);
--
fs/nfsd/filecache.c=1384=int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
--
fs/nfsd/filecache.c-1391- /* Serialize with server shutdown */
fs/nfsd/filecache.c:1392: mutex_lock(&nfsd_mutex);
fs/nfsd/filecache.c-1393- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
--
fs/nfsd/nfsctl.c=267=static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-299- error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
fs/nfsd/nfsctl.c:300: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-301- nn = net_generic(netns(file), nfsd_net_id);
--
fs/nfsd/nfsctl.c=417=static ssize_t write_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-430- trace_nfsd_ctl_threads(net, newthreads);
fs/nfsd/nfsctl.c:431: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-432- rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
--
fs/nfsd/nfsctl.c=464=static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-476-
fs/nfsd/nfsctl.c:477: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-478- npools = nfsd_nrpools(net);
--
fs/nfsd/nfsctl.c=696=static ssize_t write_versions(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-699-
fs/nfsd/nfsctl.c:700: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-701- rv = __write_versions(file, buf, size);
--
fs/nfsd/nfsctl.c=855=static ssize_t write_ports(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-858-
fs/nfsd/nfsctl.c:859: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-860- rv = __write_ports(file, buf, size, netns(file));
--
fs/nfsd/nfsctl.c=889=static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-906- bsize &= ~(1024-1);
fs/nfsd/nfsctl.c:907: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-908- if (nn->nfsd_serv) {
--
fs/nfsd/nfsctl.c=957=static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
--
fs/nfsd/nfsctl.c-961-
fs/nfsd/nfsctl.c:962: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-963- rv = __nfsd4_write_time(file, buf, size, time, nn);
--
fs/nfsd/nfsctl.c=1062=static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-1066-
fs/nfsd/nfsctl.c:1067: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1068- rv = __write_recoverydir(file, buf, size, nn);
--
fs/nfsd/nfsctl.c=1488=int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
--
fs/nfsd/nfsctl.c-1493-
fs/nfsd/nfsctl.c:1494: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1495-
--
fs/nfsd/nfsctl.c=1617=int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1632-
fs/nfsd/nfsctl.c:1633: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1634-
--
fs/nfsd/nfsctl.c=1711=int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1727-
fs/nfsd/nfsctl.c:1728: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1729-
--
fs/nfsd/nfsctl.c=1779=int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1787-
fs/nfsd/nfsctl.c:1788: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1789-
--
fs/nfsd/nfsctl.c=1847=int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1862-
fs/nfsd/nfsctl.c:1863: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1864- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfsctl.c=1927=int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1937-
fs/nfsd/nfsctl.c:1938: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1939-
--
fs/nfsd/nfsctl.c=2070=int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-2087-
fs/nfsd/nfsctl.c:2088: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2089- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfssvc.c=240=int nfsd_nrthreads(struct net *net)
--
fs/nfsd/nfssvc.c-244-
fs/nfsd/nfssvc.c:245: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-246- if (nn->nfsd_serv)
--
fs/nfsd/nfssvc.c=571=void nfsd_shutdown_threads(struct net *net)
--
fs/nfsd/nfssvc.c-575-
fs/nfsd/nfssvc.c:576: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-577- serv = nn->nfsd_serv;
]
|
| 42/2 |
2026/06/01 06:18 |
llm |
expert |
0m
Model:
Tokens:
input: 102037
output: 23
thoughts: 1394
Thoughts: **Analyzing a Potential NFSd Lockup**
Okay, let's break this down. My initial thought is about a potential deadlock involving `nfsd_mutex`. I see `nfsd_shutdown_threads` calling `mutex_lock(&nfsd_mutex)`. Could there be a situation where it's blocked? The lockdep output is there but potentially truncated, which makes it harder to diagnose.
I see several `syz-executor` tasks holding locks, specifically `syz-executor/7333`, `syz-executor/7334`, and `syz-executor/7406`. The lockdep output seems complete now because of the `=============================================`. So the task holding `nfsd_mutex` *isn't* listed! This is very strange.
How is this possible?
1. Did the task exit without unlocking? Unlikely, but possible.
2. Did it release in lockdep, but not in reality? This would be a kernel bug.
3. Is the mutex somehow not locked, but the tasks are blocked due to a bug in `mutex_lock`? That doesn't seem likely.
4. Is the task in `TASK_IDLE` state, which `debug_show_all_locks` might be filtering out? But, it iterates over all tasks, and checks `p->lockdep_depth`. If a task is idle but holding a lock, shouldn't `lockdep_depth` still be > 0?
I have to wonder, does `wait_on_bit` release the lock? No, that's not how it works.
What if `syz-executor/7406` is the one holding `nfsd_mutex`? It has `pernet_ops_rwsem` and `rtnl_mutex`.
What if the lockdep depth is zero, but the mutex is still held? Could there be a case where `mutex_release` is called, but the unlock does not actually occur? I do not see that code path.
Okay, let's shift gears. Looking at `nfsd_nl_threads_set_doit`, I see `mutex_lock(&nfsd_mutex)` again, then memory allocation with `kzalloc_objs`, which is really `kcalloc`. Could there be an allocation issue if the size is very large? The `nrpools` value is passed to `kcalloc`, and that comes from the number of server thread attributes in the Netlink message. The maximum size of a netlink message is 32KB. Each attribute is at least 8 bytes. That gives a max of 4096 attributes. `kcalloc(4096, 4)` is only 16KB. So the memory allocation size is not the issue.
Now, let's dig into `nfsd_svc`. It calls `nfsd_startup_net`, then `nfsd_set_nrthreads`. If `nfsd_set_nrthreads` hangs, then `nfsd_svc` hangs, and the mutex remains held. Why would `nfsd_set_nrthreads` hang? It calls `svc_set_pool_threads` which calls either `svc_start_kthreads` or `svc_stop_kthreads`.
`svc_stop_kthreads` is interesting. It uses a loop with `wait_on_bit` waiting for the `SP_VICTIM_REMAINS` flag to be cleared. Who clears that? The victim thread. I need to examine the `svc_exit_thread` function to understand the victim thread behavior.
|
| 43/2 |
2026/06/01 06:19 |
tool |
grepper |
0m
Args: map[Expression:SP_VICTIM_REMAINS]
Results: map[Output:include/linux/sunrpc/svc.h=54=enum {
--
include/linux/sunrpc/svc.h-56- SP_NEED_VICTIM, /* One thread needs to agree to exit */
include/linux/sunrpc/svc.h:57: SP_VICTIM_REMAINS, /* One thread needs to actually exit */
include/linux/sunrpc/svc.h-58- SP_TASK_STARTING, /* Task has started but not added to idle yet */
--
include/linux/sunrpc/svc.h=312=static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
--
include/linux/sunrpc/svc.h-320- *
include/linux/sunrpc/svc.h:321: * To stop an svc thread, the pool flags SP_NEED_VICTIM and SP_VICTIM_REMAINS
include/linux/sunrpc/svc.h-322- * are set. The first thread which sees SP_NEED_VICTIM clears it, becoming
include/linux/sunrpc/svc.h-323- * the victim using this function. It should then promptly call
include/linux/sunrpc/svc.h:324: * svc_exit_thread() to complete the process, clearing SP_VICTIM_REMAINS
include/linux/sunrpc/svc.h-325- * so the task waiting for a thread to exit can wake and continue.
--
net/sunrpc/svc.c=844=svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
--
net/sunrpc/svc.c-846- do {
net/sunrpc/svc.c:847: set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
net/sunrpc/svc.c-848- set_bit(SP_NEED_VICTIM, &pool->sp_flags);
net/sunrpc/svc.c-849- svc_pool_wake_idle_thread(pool);
net/sunrpc/svc.c:850: wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE);
net/sunrpc/svc.c-851- nrservs++;
--
net/sunrpc/svc.c=996=void svc_rqst_release_pages(struct svc_rqst *rqstp)
--
net/sunrpc/svc.c-1019- * one that is calling svc_set_num_threads() and which will wait for
net/sunrpc/svc.c:1020: * SP_VICTIM_REMAINS to be cleared before dropping the mutex. If the
net/sunrpc/svc.c-1021- * thread exits for any reason other than svc_thread_should_stop()
--
net/sunrpc/svc.c=1027=svc_exit_thread(struct svc_rqst *rqstp)
--
net/sunrpc/svc.c-1039-
net/sunrpc/svc.c:1040: clear_and_wake_up_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
net/sunrpc/svc.c-1041-}
]
|
| 44/2 |
2026/06/01 06:19 |
llm |
expert |
0m
Model:
Tokens:
input: 105090
output: 19
thoughts: 867
Thoughts: **Analyzing a Potential NFSd Lock Contention Issue**
Okay, let's break this down. First, `svc_exit_thread` clears `SP_VICTIM_REMAINS`. This makes sense. If a thread exits, we want to signal that. But, if a thread exits *other* than via a normal shutdown from `svc_thread_should_stop()`, it has to handle the service mutex itself. The comment clarifies this: usually, another thread (the one calling `svc_set_num_threads()`, which is waiting for `SP_VICTIM_REMAINS` to clear) holds the mutex.
Now, if `svc_stop_kthreads` is waiting on `SP_VICTIM_REMAINS`, that means *it* is holding `nfsd_mutex`! That's the critical point. If it *is* holding `nfsd_mutex`, it should show up in the lockdep output, right? But it's not. That's a huge red flag. Why wouldn't it?
Could it be the task is idle? Maybe `debug_show_all_locks` skips `TASK_IDLE` tasks? Let's check. It iterates over *all* threads, but it only prints locks for tasks with `p->lockdep_depth > 0`. Okay, so is `lockdep_depth` cleared when a task goes idle? No, it's only modified by `lock_acquire` and `lock_release`.
Wait, could the task holding `nfsd_mutex` be `syz.3.20:5999`? But wait! That task is blocked in `__mutex_lock_common`! If it's blocked, it *can't* be holding the mutex. Let me check the lockdep output for that task... Okay:
```
2 locks held by syz.3.20/5999:
#0: ffffffff906baec8 (cb_lock){++++}-{4:4}, at: genl_rcv+0x19/0x40 net/netlink/genetlink.c:1217
#1: ffffffff8ec62d80 (nfsd_mutex){+.+.}-{4:4}, at: nfsd_nl_threads_set_doit+0x8ec/0x12b0 fs/nfsd/nfsctl.c:1633
```
Wait, EVERY SINGLE ONE of the 30 tasks are blocked in `__mutex_lock_common`! They can't be *holding* the mutex while blocked there!
So, what about a task that has EXITED? Is it possible that `nfsd_mutex` is held by a task that's no longer running? If a task exits, `do_exit` is called, and there are many exit functions that should free locks. The important one here is `lockdep_free_task`, it *should* print a warning if a lock is held when the task exits. Does the syzkaller report contain any lockdep warnings? No, it only has this:
```
INFO: task syz.3.20:5999 blocked for more than 143 seconds.
```
No lockdep warning, so the lock isn't held by an exiting task.
Okay, is it possible that the `nfsd` thread is holding it? Let me check the `nfsd` thread.
|
| 45/2 |
2026/06/01 06:19 |
tool |
grepper |
0m
Args: map[Expression:nfsd_mutex]
Results: map[Output:Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst=39=removal of listening sockets, and startup and shutdown of the server.
Documentation/admin-guide/nfs/nfsd-admin-interfaces.rst:40:For nfsd this is done using nfsd_mutex.
--
fs/nfsd/export.c=255=static void expkey_flush(void)
--
fs/nfsd/export.c-257- /*
fs/nfsd/export.c:258: * Take the nfsd_mutex here to ensure that the file cache is not
fs/nfsd/export.c-259- * destroyed while we're in the middle of flushing.
fs/nfsd/export.c-260- */
fs/nfsd/export.c:261: mutex_lock(&nfsd_mutex);
fs/nfsd/export.c-262- nfsd_file_cache_purge(current->nsproxy->net_ns);
fs/nfsd/export.c:263: mutex_unlock(&nfsd_mutex);
fs/nfsd/export.c-264-}
--
fs/nfsd/filecache.c=776=nfsd_file_cache_init(void)
--
fs/nfsd/filecache.c-779-
fs/nfsd/filecache.c:780: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-781- if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
--
fs/nfsd/filecache.c=941=nfsd_file_cache_purge(struct net *net)
fs/nfsd/filecache.c-942-{
fs/nfsd/filecache.c:943: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-944- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
--
fs/nfsd/filecache.c=956=nfsd_file_cache_shutdown(void)
--
fs/nfsd/filecache.c-959-
fs/nfsd/filecache.c:960: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/filecache.c-961- if (test_and_clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
--
fs/nfsd/filecache.c=1384=int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
--
fs/nfsd/filecache.c-1391- /* Serialize with server shutdown */
fs/nfsd/filecache.c:1392: mutex_lock(&nfsd_mutex);
fs/nfsd/filecache.c-1393- if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1) {
--
fs/nfsd/filecache.c-1405- }
fs/nfsd/filecache.c:1406: mutex_unlock(&nfsd_mutex);
fs/nfsd/filecache.c-1407-
--
fs/nfsd/nfsctl.c=267=static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-299- error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
fs/nfsd/nfsctl.c:300: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-301- nn = net_generic(netns(file), nfsd_net_id);
--
fs/nfsd/nfsctl.c-305- error = -EINVAL;
fs/nfsd/nfsctl.c:306: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-307-
--
fs/nfsd/nfsctl.c=417=static ssize_t write_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-430- trace_nfsd_ctl_threads(net, newthreads);
fs/nfsd/nfsctl.c:431: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-432- rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
fs/nfsd/nfsctl.c:433: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-434- if (rv < 0)
--
fs/nfsd/nfsctl.c=464=static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-476-
fs/nfsd/nfsctl.c:477: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-478- npools = nfsd_nrpools(net);
--
fs/nfsd/nfsctl.c-484- */
fs/nfsd/nfsctl.c:485: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-486- strcpy(buf, "0\n");
--
fs/nfsd/nfsctl.c-534- kfree(nthreads);
fs/nfsd/nfsctl.c:535: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-536- return rv;
--
fs/nfsd/nfsctl.c=696=static ssize_t write_versions(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-699-
fs/nfsd/nfsctl.c:700: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-701- rv = __write_versions(file, buf, size);
fs/nfsd/nfsctl.c:702: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-703- return rv;
--
fs/nfsd/nfsctl.c=855=static ssize_t write_ports(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-858-
fs/nfsd/nfsctl.c:859: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-860- rv = __write_ports(file, buf, size, netns(file));
fs/nfsd/nfsctl.c:861: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-862- return rv;
--
fs/nfsd/nfsctl.c=889=static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-906- bsize &= ~(1024-1);
fs/nfsd/nfsctl.c:907: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-908- if (nn->nfsd_serv) {
fs/nfsd/nfsctl.c:909: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-910- return -EBUSY;
--
fs/nfsd/nfsctl.c-912- nfsd_max_blksize = bsize;
fs/nfsd/nfsctl.c:913: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-914- }
--
fs/nfsd/nfsctl.c=957=static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size,
--
fs/nfsd/nfsctl.c-961-
fs/nfsd/nfsctl.c:962: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-963- rv = __nfsd4_write_time(file, buf, size, time, nn);
fs/nfsd/nfsctl.c:964: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-965- return rv;
--
fs/nfsd/nfsctl.c=1062=static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
--
fs/nfsd/nfsctl.c-1066-
fs/nfsd/nfsctl.c:1067: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1068- rv = __write_recoverydir(file, buf, size, nn);
fs/nfsd/nfsctl.c:1069: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1070- return rv;
--
fs/nfsd/nfsctl.c=1488=int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
--
fs/nfsd/nfsctl.c-1493-
fs/nfsd/nfsctl.c:1494: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1495-
--
fs/nfsd/nfsctl.c-1578-out_unlock:
fs/nfsd/nfsctl.c:1579: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1580-
--
fs/nfsd/nfsctl.c-1588- *
fs/nfsd/nfsctl.c:1589: * Callers should hold nfsd_mutex, returns 0 on success or negative errno.
fs/nfsd/nfsctl.c-1590- * Callers must ensure the server is shut down (sv_nrthreads == 0),
--
fs/nfsd/nfsctl.c=1617=int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1632-
fs/nfsd/nfsctl.c:1633: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1634-
--
fs/nfsd/nfsctl.c-1698-out_unlock:
fs/nfsd/nfsctl.c:1699: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1700- kfree(nthreads);
--
fs/nfsd/nfsctl.c=1711=int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1727-
fs/nfsd/nfsctl.c:1728: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1729-
--
fs/nfsd/nfsctl.c-1757-
fs/nfsd/nfsctl.c:1758: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1759-
--
fs/nfsd/nfsctl.c-1764-err_unlock:
fs/nfsd/nfsctl.c:1765: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1766-err_free_msg:
--
fs/nfsd/nfsctl.c=1779=int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1787-
fs/nfsd/nfsctl.c:1788: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1789-
--
fs/nfsd/nfsctl.c-1791- if (nn->nfsd_serv) {
fs/nfsd/nfsctl.c:1792: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1793- return -EBUSY;
--
fs/nfsd/nfsctl.c-1834-
fs/nfsd/nfsctl.c:1835: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1836-
--
fs/nfsd/nfsctl.c=1847=int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1862-
fs/nfsd/nfsctl.c:1863: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1864- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfsctl.c-1906-
fs/nfsd/nfsctl.c:1907: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1908- genlmsg_end(skb, hdr);
--
fs/nfsd/nfsctl.c-1912-err_nfsd_unlock:
fs/nfsd/nfsctl.c:1913: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1914-err_free_msg:
--
fs/nfsd/nfsctl.c=1927=int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-1937-
fs/nfsd/nfsctl.c:1938: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1939-
--
fs/nfsd/nfsctl.c-1941- if (err) {
fs/nfsd/nfsctl.c:1942: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-1943- return err;
--
fs/nfsd/nfsctl.c-2057-out_unlock_mtx:
fs/nfsd/nfsctl.c:2058: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2059-
--
fs/nfsd/nfsctl.c=2070=int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
--
fs/nfsd/nfsctl.c-2087-
fs/nfsd/nfsctl.c:2088: mutex_lock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2089- nn = net_generic(genl_info_net(info), nfsd_net_id);
--
fs/nfsd/nfsctl.c-2118-out_unlock_mtx:
fs/nfsd/nfsctl.c:2119: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2120- genlmsg_end(skb, hdr);
--
fs/nfsd/nfsctl.c-2125- spin_unlock_bh(&serv->sv_lock);
fs/nfsd/nfsctl.c:2126: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfsctl.c-2127-err_free_msg:
--
fs/nfsd/nfsctl.c=2200=static __net_init int nfsd_net_init(struct net *net)
--
fs/nfsd/nfsctl.c-2230- nn->nfsd4_minorversions[i] = nfsd_support_version(4);
fs/nfsd/nfsctl.c:2231: nn->nfsd_info.mutex = &nfsd_mutex;
fs/nfsd/nfsctl.c-2232- nn->nfsd_serv = NULL;
--
fs/nfsd/nfsd.h=79=extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
fs/nfsd/nfsd.h:80:extern struct mutex nfsd_mutex;
fs/nfsd/nfsd.h-81-extern atomic_t nfsd_th_cnt; /* number of available threads */
--
fs/nfsd/nfssvc.c=55=static __be32 nfsd_init_request(struct svc_rqst *,
--
fs/nfsd/nfssvc.c-59-/*
fs/nfsd/nfssvc.c:60: * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
fs/nfsd/nfssvc.c-61- * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
fs/nfsd/nfssvc.c-62- *
fs/nfsd/nfssvc.c:63: * Finally, the nfsd_mutex also protects some of the global variables that are
fs/nfsd/nfssvc.c-64- * accessed when nfsd starts and that are settable via the write_* routines in
--
fs/nfsd/nfssvc.c-70- */
fs/nfsd/nfssvc.c:71:DEFINE_MUTEX(nfsd_mutex);
fs/nfsd/nfssvc.c-72-
--
fs/nfsd/nfssvc.c=240=int nfsd_nrthreads(struct net *net)
--
fs/nfsd/nfssvc.c-244-
fs/nfsd/nfssvc.c:245: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-246- if (nn->nfsd_serv)
--
fs/nfsd/nfssvc.c-248- rv += nn->nfsd_serv->sv_pools[i].sp_nrthrmax;
fs/nfsd/nfssvc.c:249: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-250- return rv;
--
fs/nfsd/nfssvc.c=490=static struct notifier_block nfsd_inet6addr_notifier = {
--
fs/nfsd/nfssvc.c-494-
fs/nfsd/nfssvc.c:495:/* Only used under nfsd_mutex, so this atomic may be overkill: */
fs/nfsd/nfssvc.c-496-static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
--
fs/nfsd/nfssvc.c=502=void nfsd_destroy_serv(struct net *net)
--
fs/nfsd/nfssvc.c-506-
fs/nfsd/nfssvc.c:507: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-508-
--
fs/nfsd/nfssvc.c=571=void nfsd_shutdown_threads(struct net *net)
--
fs/nfsd/nfssvc.c-575-
fs/nfsd/nfssvc.c:576: mutex_lock(&nfsd_mutex);
fs/nfsd/nfssvc.c-577- serv = nn->nfsd_serv;
fs/nfsd/nfssvc.c-578- if (serv == NULL) {
fs/nfsd/nfssvc.c:579: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-580- return;
--
fs/nfsd/nfssvc.c-585- nfsd_destroy_serv(net);
fs/nfsd/nfssvc.c:586: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-587-}
--
fs/nfsd/nfssvc.c=596=int nfsd_create_serv(struct net *net)
--
fs/nfsd/nfssvc.c-601-
fs/nfsd/nfssvc.c:602: WARN_ON(!mutex_is_locked(&nfsd_mutex));
fs/nfsd/nfssvc.c-603- if (nn->nfsd_serv)
--
fs/nfsd/nfssvc.c=680=int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
--
fs/nfsd/nfssvc.c-686-
fs/nfsd/nfssvc.c:687: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-688-
--
fs/nfsd/nfssvc.c=751=nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
--
fs/nfsd/nfssvc.c-756-
fs/nfsd/nfssvc.c:757: lockdep_assert_held(&nfsd_mutex);
fs/nfsd/nfssvc.c-758-
--
fs/nfsd/nfssvc.c=883=nfsd(void *vrqstp)
--
fs/nfsd/nfssvc.c-915- /* No work arrived within the timeout window */
fs/nfsd/nfssvc.c:916: if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c-917- if (pool->sp_nrthreads > pool->sp_nrthrmin) {
--
fs/nfsd/nfssvc.c-921- } else {
fs/nfsd/nfssvc.c:922: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-923- }
--
fs/nfsd/nfssvc.c-930- if (pool->sp_nrthreads < pool->sp_nrthrmax) {
fs/nfsd/nfssvc.c:931: if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c-932- if (pool->sp_nrthreads < pool->sp_nrthrmax) {
--
fs/nfsd/nfssvc.c-940- }
fs/nfsd/nfssvc.c:941: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-942- } else {
--
fs/nfsd/nfssvc.c-958- if (have_mutex)
fs/nfsd/nfssvc.c:959: mutex_unlock(&nfsd_mutex);
fs/nfsd/nfssvc.c-960- return 0;
]
|
| 46/2 |
2026/06/01 06:19 |
llm |
expert |
0m
Model:
Tokens:
input: 113197
output: 20
thoughts: 2299
Thoughts: **Debugging a NFSd Mutex Contention**
Okay, so I'm looking at this `nfsd_shutdown_threads` function. It calls `mutex_lock(&nfsd_mutex)`, and I'm thinking, could this be the source of a block? It should be in the lockdep output if it is, but it's not. Alright, what if the task *is* in `TASK_RUNNING` but not on a CPU? If it's running it should still be in lockdep. Okay, what if it is `syz-executor/7333`? Let me re-check the lockdep output...nope. That executor holds `rtnl_mutex`, `wg->device_update_lock`, and `rcu_state.exp_mutex` not the `nfsd_mutex`. Alright, what about `syz-executor/7334`? Nope, not that one either. Or how about `syz-executor/7406`? No, that holds `pernet_ops_rwsem` and `rtnl_mutex`, but not `nfsd_mutex`. Is it `kworker/u8:10/1174`? Still a no. `getty/5368`? Nope. `kworker/1:6/5964`? Still nothing. `khungtaskd/30`? No dice.
So it looks like NO task holds `nfsd_mutex`. But, there are 30 tasks blocked in `__mutex_lock_common`! But that's the slow path, and several tasks can be trying to acquire a mutex at the same time. If it were held, it would show up in the lockdep output, but it doesn't. Okay, let's look at the lockdep output for *those* 30 tasks...
Wait a second! Lockdep shows `nfsd_mutex` as HELD by *every* one of those 30 tasks! And in each case, it shows it being held by some `syz.*` process. That is impossible - a mutex can only be held by one task at a time!
But, is it actually a mutex? Let me check the code... Yep, `DEFINE_MUTEX(nfsd_mutex)`. It is a mutex. So why are all these tasks showing up as holding it? Ah, right. If those tasks are all blocked within `__mutex_lock_common`, lockdep adds the lock *before* they sleep. I see. Let me look at the code for `__mutex_lock_common` in `kernel/locking/mutex.c` again...
|
| 47/2 |
2026/06/01 06:19 |
tool |
grepper |
0m
Args: map[Expression:__mutex_lock_common]
Results: map[Output:kernel/locking/mutex.c=608=static __always_inline int __sched
kernel/locking/mutex.c:609:__mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclass,
kernel/locking/mutex.c-610- struct lockdep_map *nest_lock, unsigned long ip,
--
kernel/locking/mutex.c=816=__mutex_lock(struct mutex *lock, unsigned int state, unsigned int subclass,
--
kernel/locking/mutex.c-819-{
kernel/locking/mutex.c:820: return __mutex_lock_common(lock, state, subclass, nest_lock, ip, NULL, false);
kernel/locking/mutex.c-821-}
--
kernel/locking/mutex.c=824=__ww_mutex_lock(struct mutex *lock, unsigned int state, unsigned int subclass,
--
kernel/locking/mutex.c-827-{
kernel/locking/mutex.c:828: return __mutex_lock_common(lock, state, subclass, NULL, ip, ww_ctx, true);
kernel/locking/mutex.c-829-}
--
kernel/locking/mutex.c=903=mutex_lock_io_nested(struct mutex *lock, unsigned int subclass)
--
kernel/locking/mutex.c-909- token = io_schedule_prepare();
kernel/locking/mutex.c:910: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE,
kernel/locking/mutex.c-911- subclass, NULL, _RET_IP_, NULL, 0);
--
kernel/locking/rtmutex_api.c=518=static void __mutex_rt_init_generic(struct mutex *mutex)
--
kernel/locking/rtmutex_api.c-523-
kernel/locking/rtmutex_api.c:524:static __always_inline int __mutex_lock_common(struct mutex *lock,
kernel/locking/rtmutex_api.c-525- unsigned int state,
--
kernel/locking/rtmutex_api.c=551=void __sched mutex_lock_nested(struct mutex *lock, unsigned int subclass)
kernel/locking/rtmutex_api.c-552-{
kernel/locking/rtmutex_api.c:553: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, subclass, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-554-}
--
kernel/locking/rtmutex_api.c=557=void __sched _mutex_lock_nest_lock(struct mutex *lock,
--
kernel/locking/rtmutex_api.c-559-{
kernel/locking/rtmutex_api.c:560: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, nest_lock, _RET_IP_);
kernel/locking/rtmutex_api.c-561-}
--
kernel/locking/rtmutex_api.c=564=int __sched mutex_lock_interruptible_nested(struct mutex *lock,
--
kernel/locking/rtmutex_api.c-566-{
kernel/locking/rtmutex_api.c:567: return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, subclass, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-568-}
--
kernel/locking/rtmutex_api.c=571=int __sched _mutex_lock_killable(struct mutex *lock, unsigned int subclass,
--
kernel/locking/rtmutex_api.c-573-{
kernel/locking/rtmutex_api.c:574: return __mutex_lock_common(lock, TASK_KILLABLE, subclass, nest_lock, _RET_IP_);
kernel/locking/rtmutex_api.c-575-}
--
kernel/locking/rtmutex_api.c=578=void __sched mutex_lock_io_nested(struct mutex *lock, unsigned int subclass)
--
kernel/locking/rtmutex_api.c-584- token = io_schedule_prepare();
kernel/locking/rtmutex_api.c:585: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, subclass, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-586- io_schedule_finish(token);
--
kernel/locking/rtmutex_api.c=613=void __sched mutex_lock(struct mutex *lock)
kernel/locking/rtmutex_api.c-614-{
kernel/locking/rtmutex_api.c:615: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-616-}
--
kernel/locking/rtmutex_api.c=619=int __sched mutex_lock_interruptible(struct mutex *lock)
kernel/locking/rtmutex_api.c-620-{
kernel/locking/rtmutex_api.c:621: return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, 0, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-622-}
--
kernel/locking/rtmutex_api.c=625=int __sched mutex_lock_killable(struct mutex *lock)
kernel/locking/rtmutex_api.c-626-{
kernel/locking/rtmutex_api.c:627: return __mutex_lock_common(lock, TASK_KILLABLE, 0, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-628-}
--
kernel/locking/rtmutex_api.c=631=void __sched mutex_lock_io(struct mutex *lock)
--
kernel/locking/rtmutex_api.c-634-
kernel/locking/rtmutex_api.c:635: __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, NULL, _RET_IP_);
kernel/locking/rtmutex_api.c-636- io_schedule_finish(token);
]
|
| 48/2 |
2026/06/01 06:19 |
llm |
expert |
0m
Model:
Tokens:
input: 117646
output: 35
thoughts: 43
Thoughts: Okay, let's see...
**Analyzing the Mutex Locking Mechanism**
Hold on a second... the implementation of `__mutex_lock_common` is calling `mutex_acquire` *before* it goes to sleep? That's not the order I was expecting. It's usually the other way around. I need to double-check my understanding here. I should read through `__mutex_lock_common` in `kernel/locking/mutex.c` now to understand the sequence of events. Let me get to that code... there might be something important I've missed in my mental model of the locking primitives.
|
| 49/2 |
2026/06/01 06:19 |
tool |
read-file |
0m
Args: map[File:kernel/locking/mutex.c FirstLine:608 LineCount:50]
Results: map[Contents: 608: static __always_inline int __sched
609: __mutex_lock_common(struct mutex *lock, unsigned int state, unsigned int subclass,
610: struct lockdep_map *nest_lock, unsigned long ip,
611: struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)
612: __cond_acquires(0, lock)
613: {
614: DEFINE_WAKE_Q(wake_q);
615: struct mutex_waiter waiter;
616: struct ww_mutex *ww;
617: unsigned long flags;
618: int ret;
619:
620: if (!use_ww_ctx)
621: ww_ctx = NULL;
622:
623: might_sleep();
624:
625: MUTEX_WARN_ON(lock->magic != lock);
626:
627: ww = container_of(lock, struct ww_mutex, base);
628: if (ww_ctx) {
629: if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))
630: return -EALREADY;
631:
632: /*
633: * Reset the wounded flag after a kill. No other process can
634: * race and wound us here since they can't have a valid owner
635: * pointer if we don't have any locks held.
636: */
637: if (ww_ctx->acquired == 0)
638: ww_ctx->wounded = 0;
639:
640: #ifdef CONFIG_DEBUG_LOCK_ALLOC
641: nest_lock = &ww_ctx->dep_map;
642: #endif
643: }
644:
645: preempt_disable();
646: mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip);
647:
648: trace_contention_begin(lock, LCB_F_MUTEX | LCB_F_SPIN);
649: if (__mutex_trylock(lock) ||
650: mutex_optimistic_spin(lock, ww_ctx, NULL)) {
651: /* got the lock, yay! */
652: lock_acquired(&lock->dep_map, ip);
653: if (ww_ctx)
654: ww_mutex_set_context_fastpath(ww, ww_ctx);
655: trace_contention_end(lock, 0);
656: preempt_enable();
657: return 0;
]
|
| 50/2 |
2026/06/01 06:19 |
llm |
expert |
0m
Model:
Tokens:
input: 118635
output: 36
thoughts: 118
Thoughts: Okay, here's my understanding, based on those breadcrumbs.
**Digging into Mutex Acquisition Logic**
Right, so it looks like `mutex_acquire_nest` gets called *before* the actual attempts at acquisition using `__mutex_trylock` or the optimistic spin in `mutex_optimistic_spin`. That's important. I see, if `__mutex_trylock` fails—a pretty standard early-out scenario—then we're going down the slow path. Now I remember why I was looking here, I want to clarify nested locking behaviors.
Aha! `mutex_acquire_nest` is adding the lock to the task's "held locks" list. That's a key part of the nesting logic. So, if this acquisition fails, meaning it *can't* acquire the lock right away, the task ends up going to sleep. That seems like the expected behavior for contended locks.
But hang on… if the task is going to sleep, what happens to that entry in the held locks list? Does the function *remove* the lock from the list before going to sleep? Or is it left there? The way the lock is *presented* to the other threads is determined by how it's represented. I better dig into `__mutex_lock_common` to confirm that behavior. That's the core of how this all works. Let's see...
|
| 51/2 |
2026/06/01 06:20 |
tool |
read-file |
0m
Args: map[File:kernel/locking/mutex.c FirstLine:658 LineCount:100]
Results: map[Contents: 658: }
659:
660: raw_spin_lock_irqsave(&lock->wait_lock, flags);
661: /*
662: * After waiting to acquire the wait_lock, try again.
663: */
664: if (__mutex_trylock(lock)) {
665: if (ww_ctx)
666: __ww_mutex_check_waiters(lock, ww_ctx, &wake_q);
667:
668: goto skip_wait;
669: }
670:
671: debug_mutex_lock_common(lock, &waiter);
672: waiter.task = current;
673: if (use_ww_ctx)
674: waiter.ww_ctx = ww_ctx;
675:
676: lock_contended(&lock->dep_map, ip);
677:
678: if (!use_ww_ctx) {
679: /* add waiting tasks to the end of the waitqueue (FIFO): */
680: __mutex_add_waiter(lock, &waiter, NULL);
681: } else {
682: /*
683: * Add in stamp order, waking up waiters that must kill
684: * themselves.
685: */
686: ret = __ww_mutex_add_waiter(&waiter, lock, ww_ctx, &wake_q);
687: if (ret)
688: goto err_early_kill;
689: }
690:
691: raw_spin_lock(¤t->blocked_lock);
692: __set_task_blocked_on(current, lock);
693: set_current_state(state);
694: trace_contention_begin(lock, LCB_F_MUTEX);
695: for (;;) {
696: bool first;
697:
698: /*
699: * Once we hold wait_lock, we're serialized against
700: * mutex_unlock() handing the lock off to us, do a trylock
701: * before testing the error conditions to make sure we pick up
702: * the handoff.
703: */
704: if (__mutex_trylock(lock))
705: break;
706:
707: raw_spin_unlock(¤t->blocked_lock);
708: /*
709: * Check for signals and kill conditions while holding
710: * wait_lock. This ensures the lock cancellation is ordered
711: * against mutex_unlock() and wake-ups do not go missing.
712: */
713: if (signal_pending_state(state, current)) {
714: ret = -EINTR;
715: goto err;
716: }
717:
718: if (ww_ctx) {
719: ret = __ww_mutex_check_kill(lock, &waiter, ww_ctx);
720: if (ret)
721: goto err;
722: }
723:
724: raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q);
725:
726: schedule_preempt_disabled();
727:
728: first = lock->first_waiter == &waiter;
729:
730: raw_spin_lock_irqsave(&lock->wait_lock, flags);
731: raw_spin_lock(¤t->blocked_lock);
732: /*
733: * As we likely have been woken up by task
734: * that has cleared our blocked_on state, re-set
735: * it to the lock we are trying to acquire.
736: */
737: __set_task_blocked_on(current, lock);
738: set_current_state(state);
739: /*
740: * Here we order against unlock; we must either see it change
741: * state back to RUNNING and fall through the next schedule(),
742: * or we must see its unlock and acquire.
743: */
744: if (__mutex_trylock_or_handoff(lock, first))
745: break;
746:
747: if (first) {
748: bool opt_acquired;
749:
750: /*
751: * mutex_optimistic_spin() can call schedule(), so
752: * we need to release these locks before calling it,
753: * and clear blocked on so we don't become unselectable
754: * to run.
755: */
756: __clear_task_blocked_on(current, lock);
757: raw_spin_unlock(¤t->blocked_lock);
]
|
| 52/2 |
2026/06/01 06:20 |
llm |
expert |
0m
Model:
Tokens:
input: 120521
output: 20
thoughts: 2457
Thoughts: **The Puzzle of the Persistent Mutex**
Okay, here's the situation I've pieced together: It looks like `mutex_acquire_nest` correctly adds locks to the held locks list, so even when a task is sleeping, `lockdep` will show it as holding the lock. This explains why we see all those tasks claiming to hold `nfsd_mutex`—they're all waiting for it. But one of them actually holds it. The lock must be held by a task that acquired it but never released it, since all the waiting ones are in `schedule_preempt_disabled()`.
Initially, I considered a scenario where `nfsd_mutex` wasn't locked or a truncated lockdep output, but I dismissed them. Could the holding task be a gone, exited task? I investigated, and considered the possibility of a "lost" lockdep warning, but that's unlikely in this scenario. I also checked for coding errors that could release the lock in reality but not from the perspective of lockdep—not the case. Lockdep can be trusted here.
I thought, is it a lockdep bug? No. Or could it be an `nfsd` thread holding it? Also no, if it's held, it SHOULD be in lockdep. I even considered the use of `lockdep_off()` after the acquisition, but that doesn't fit either.
I then went through the `nfsd_nl_threads_set_doit` code, but I couldn't find a path that would fail to unlock the mutex. All error paths correctly release the mutex. I started to wonder if `nfsd_svc` itself was hanging. If `nfsd_svc` hangs, the calling task would hold the mutex forever. The syzkaller report *hints* at this, but it truncates the list of hung tasks to save space! I realized that it's possible that *one* of the 30 tasks reported in the lockdep output is actually the task *inside* `nfsd_svc` and is actually hung there.
However, the lockdep output is showing `nfsd_nl_threads_set_doit+0x8ec/0x12b0` for *all* the tasks, so a task holding the lock would have the same backtrace! This means the lockdep is showing where the lock was ACQUIRED.
Then it clicked. The syzkaller report does not include this task's stack trace because of the state of the thread! The hung task detector *only* prints stack traces of tasks that are in the `D` state—that is, `TASK_UNINTERRUPTIBLE`. My thinking is that the task holding the mutex is in a state other than `D` state, like `TASK_IDLE`. Specifically, it is stuck in `svc_stop_kthreads`, waiting for something. If it's blocked in `svc_stop_kthreads`, it's not being checked by the hung task detector! `svc_stop_kthreads` uses `wait_on_bit` with `TASK_IDLE`!
This explains it. The task is holding `nfsd_mutex` inside `svc_stop_kthreads`, waiting for a thread to exit. I need to figure out *why* a thread isn't exiting. That is the problem.
|
| 53/2 |
2026/06/01 06:20 |
tool |
grepper |
0m
Args: map[Expression:svc_stop_kthreads]
Results: map[Output:net/sunrpc/svc.c=843=static int
net/sunrpc/svc.c:844:svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
net/sunrpc/svc.c-845-{
--
net/sunrpc/svc.c=876=svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
--
net/sunrpc/svc.c-909- if (delta < 0)
net/sunrpc/svc.c:910: return svc_stop_kthreads(serv, pool, delta);
net/sunrpc/svc.c-911- return 0;
]
|
| 54/2 |
2026/06/01 06:20 |
llm |
expert |
0m
Model:
Tokens:
input: 123913
output: 23
thoughts: 344
Thoughts: **Analyzing `svc_stop_kthreads` for Thread Pool Termination**
Okay, let's dive back into `svc_stop_kthreads` and walk through this logic again. I need to make sure I fully understand the thread pool shutdown sequence. First, the code sets `SP_VICTIM_REMAINS` and `SP_NEED_VICTIM` within the pool flags, then calls `svc_pool_wake_idle_thread`. That seems straightforward – signal a thread to wake up and handle the shutdown. The critical part is the wait: it blocks on `SP_VICTIM_REMAINS`. The intent is for the woken thread to clear this flag when it's done.
My immediate question is: what if the pool is *completely* empty? The code seems to assume there are always threads to be woken. The `do...while` loop looks a bit odd with `nrservs` being incremented, but hold on - `nrservs` is negative to start with! Hmm, let me think this through, `delta = max_threads - pool->sp_nrthreads;`. If `delta` is negative, that means `pool->sp_nrthreads` is *greater* than `max_threads`, but that should not be the case. So, the `do...while` must iterate at least once to ensure there are still threads in the pool, and that should imply there are threads.
But, hang on, what if `svc_pool_wake_idle_thread(pool)` wakes a thread, but that thread *doesn't* exit? That is a potential problem. I need to check how `svc_pool_wake_idle_thread` itself works. Let me examine the source for that function.
|
| 55/2 |
2026/06/01 06:20 |
tool |
grepper |
0m
Args: map[Expression:svc_pool_wake_idle_thread]
Results: map[Output:include/linux/sunrpc/svc.h=479=void svc_reserve(struct svc_rqst *rqstp, int space);
include/linux/sunrpc/svc.h:480:void svc_pool_wake_idle_thread(struct svc_pool *pool);
include/linux/sunrpc/svc.h-481-struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv);
--
net/sunrpc/backchannel_rqst.c=385=void xprt_enqueue_bc_request(struct rpc_rqst *req)
--
net/sunrpc/backchannel_rqst.c-394- lwq_enqueue(&req->rq_bc_list, &bc_serv->sv_cb_list);
net/sunrpc/backchannel_rqst.c:395: svc_pool_wake_idle_thread(&bc_serv->sv_pools[0]);
net/sunrpc/backchannel_rqst.c-396- }
--
net/sunrpc/svc.c=701=svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
--
net/sunrpc/svc.c-752-/**
net/sunrpc/svc.c:753: * svc_pool_wake_idle_thread - Awaken an idle thread in @pool
net/sunrpc/svc.c-754- * @pool: service thread pool
--
net/sunrpc/svc.c-757- * service thread and marking it BUSY is atomic with respect to
net/sunrpc/svc.c:758: * other calls to svc_pool_wake_idle_thread().
net/sunrpc/svc.c-759- *
net/sunrpc/svc.c-760- */
net/sunrpc/svc.c:761:void svc_pool_wake_idle_thread(struct svc_pool *pool)
net/sunrpc/svc.c-762-{
--
net/sunrpc/svc.c-783-}
net/sunrpc/svc.c:784:EXPORT_SYMBOL_GPL(svc_pool_wake_idle_thread);
net/sunrpc/svc.c-785-
--
net/sunrpc/svc.c=844=svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
--
net/sunrpc/svc.c-848- set_bit(SP_NEED_VICTIM, &pool->sp_flags);
net/sunrpc/svc.c:849: svc_pool_wake_idle_thread(pool);
net/sunrpc/svc.c-850- wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE);
--
net/sunrpc/svc_xprt.c=473=void svc_xprt_enqueue(struct svc_xprt *xprt)
--
net/sunrpc/svc_xprt.c-493-
net/sunrpc/svc_xprt.c:494: svc_pool_wake_idle_thread(pool);
net/sunrpc/svc_xprt.c-495-}
--
net/sunrpc/svc_xprt.c=586=void svc_wake_up(struct svc_serv *serv)
--
net/sunrpc/svc_xprt.c-590- set_bit(SP_TASK_PENDING, &pool->sp_flags);
net/sunrpc/svc_xprt.c:591: svc_pool_wake_idle_thread(pool);
net/sunrpc/svc_xprt.c-592-}
--
net/sunrpc/svc_xprt.c=861=static void svc_thread_wake_next(struct svc_rqst *rqstp)
--
net/sunrpc/svc_xprt.c-866- */
net/sunrpc/svc_xprt.c:867: svc_pool_wake_idle_thread(rqstp->rq_pool);
net/sunrpc/svc_xprt.c-868-}
]
|
| 56/2 |
2026/06/01 06:20 |
llm |
expert |
1m
Model:
Tokens:
input: 125719
output: 36
thoughts: 26
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `svc_pool_wake_idle_thread` in `net/sunrpc/svc.c`**
Alright, time to dig into the RPC machinery again. I'm focusing on the idling thread management, specifically the `svc_pool_wake_idle_thread` function within the `net/sunrpc/svc.c` file. I'm betting this is where the core logic resides for re-activating those threads when a service request comes in.
My initial hypothesis is that it likely involves signaling a condition variable or perhaps modifying a shared state variable that the idle threads are polling. I'm interested in the underlying synchronization primitives used – mutexes, semaphores, atomic operations – and how they're used to ensure thread safety and prevent race conditions. How does it handle potential spurious wake-ups? Does it have any kind of priority or fairness mechanism, or is it a simple FIFO wake-up? And critically, I want to understand the *precise* relationship between the idle threads, the incoming request handling, and this specific `svc_pool_wake_idle_thread` function. Is it directly called by the request handler, or via some intermediate queueing or dispatch mechanism?
The goal is to understand how well-tuned this is, and if there are any opportunities for further optimization or potential bottlenecks related to thread wake-up latency or contention. Also, I wonder if the approach is suitable for higher concurrency scenarios or if it needs tuning when facing many incoming requests.
|
| 57/2 |
2026/06/01 06:21 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc.c FirstLine:752 LineCount:50]
Results: map[Contents: 752: /**
753: * svc_pool_wake_idle_thread - Awaken an idle thread in @pool
754: * @pool: service thread pool
755: *
756: * Can be called from soft IRQ or process context. Finding an idle
757: * service thread and marking it BUSY is atomic with respect to
758: * other calls to svc_pool_wake_idle_thread().
759: *
760: */
761: void svc_pool_wake_idle_thread(struct svc_pool *pool)
762: {
763: struct svc_rqst *rqstp;
764: struct llist_node *ln;
765:
766: rcu_read_lock();
767: ln = READ_ONCE(pool->sp_idle_threads.first);
768: if (ln) {
769: rqstp = llist_entry(ln, struct svc_rqst, rq_idle);
770: WRITE_ONCE(rqstp->rq_qtime, ktime_get());
771: if (!task_is_running(rqstp->rq_task)) {
772: wake_up_process(rqstp->rq_task);
773: trace_svc_pool_thread_wake(pool, rqstp->rq_task->pid);
774: percpu_counter_inc(&pool->sp_threads_woken);
775: } else {
776: trace_svc_pool_thread_running(pool, rqstp->rq_task->pid);
777: }
778: rcu_read_unlock();
779: return;
780: }
781: rcu_read_unlock();
782: trace_svc_pool_thread_noidle(pool, 0);
783: }
784: EXPORT_SYMBOL_GPL(svc_pool_wake_idle_thread);
785:
786: /**
787: * svc_new_thread - spawn a new thread in the given pool
788: * @serv: the serv to which the pool belongs
789: * @pool: pool in which thread should be spawned
790: *
791: * Create a new thread inside @pool, which is a part of @serv.
792: * Caller must hold the service mutex.
793: *
794: * Returns 0 on success, or -errno on failure.
795: */
796: int svc_new_thread(struct svc_serv *serv, struct svc_pool *pool)
797: {
798: struct svc_rqst *rqstp;
799: struct task_struct *task;
800: int node;
801: int err = 0;
]
|
| 58/2 |
2026/06/01 06:21 |
llm |
expert |
0m
Model:
Tokens:
input: 126920
output: 21
thoughts: 536
Thoughts: **Analyzing the Thread Pool Wakeup and Termination Logic**
Okay, let's break this down. My initial thought is about how `svc_pool_wake_idle_thread` works. It wakes the *first* idle thread, which seems straightforward. But if *no* threads are idle, it does nothing. That means `wait_on_bit` could hang indefinitely because nothing is going to become idle, right? Wait, if there are no idle threads, that implies all threads are actively handling requests. Those threads *should* eventually finish and become idle. And when they do, they'll check `SP_NEED_VICTIM` and exit.
But what if a thread gets stuck processing a request, hangs, and *never* becomes idle? In that case, `svc_stop_kthreads` will wait forever. Hmm, that scenario *should* be detectable through lockdep or hung task reports, hopefully.
Now, what if there are *no* threads in the pool to begin with? If `pool->sp_nrthreads` is zero, then `delta` becomes `max_threads` when calculating the number of threads to add or remove. If `max_threads` is positive, then `delta > 0`, and `svc_start_kthreads` will be called to launch the thread(s). If `max_threads` is zero, then `delta == 0`, and nothing happens. So, `svc_stop_kthreads` is *only* called when `delta < 0`, meaning `pool->sp_nrthreads > max_threads`. Because `max_threads >= 0`, `pool->sp_nrthreads` *must* be positive. This means there's at least one thread in the pool. But why wouldn't it wake up, then?
Let's check out `svc_stop_kthreads`. Here's the relevant loop:
```c
do {
set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
set_bit(SP_NEED_VICTIM, &pool->sp_flags);
svc_pool_wake_idle_thread(pool);
wait_on_bit(&pool->sp_flags, SP_VICTIM_REMAINS, TASK_IDLE);
nrservs++;
} while (nrservs < 0);
```
Wait, what if `svc_pool_wake_idle_thread` wakes up a thread, but the thread *doesn't* see `SP_NEED_VICTIM`? That would mean it doesn't know it should exit. I need to trace this. I should check where the thread actually tests the `SP_NEED_VICTIM` flag, probably in `svc_recv` or some other service processing loop. That's the key.
|
| 59/2 |
2026/06/01 06:21 |
tool |
grepper |
0m
Args: map[Expression:SP_NEED_VICTIM]
Results: map[Output:include/linux/sunrpc/svc.h=54=enum {
include/linux/sunrpc/svc.h-55- SP_TASK_PENDING, /* still work to do even if no xprt is queued */
include/linux/sunrpc/svc.h:56: SP_NEED_VICTIM, /* One thread needs to agree to exit */
include/linux/sunrpc/svc.h-57- SP_VICTIM_REMAINS, /* One thread needs to actually exit */
--
include/linux/sunrpc/svc.h=312=static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
--
include/linux/sunrpc/svc.h-320- *
include/linux/sunrpc/svc.h:321: * To stop an svc thread, the pool flags SP_NEED_VICTIM and SP_VICTIM_REMAINS
include/linux/sunrpc/svc.h:322: * are set. The first thread which sees SP_NEED_VICTIM clears it, becoming
include/linux/sunrpc/svc.h-323- * the victim using this function. It should then promptly call
--
include/linux/sunrpc/svc.h=331=static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
include/linux/sunrpc/svc.h-332-{
include/linux/sunrpc/svc.h:333: if (test_and_clear_bit(SP_NEED_VICTIM, &rqstp->rq_pool->sp_flags))
include/linux/sunrpc/svc.h-334- set_bit(RQ_VICTIM, &rqstp->rq_flags);
--
net/sunrpc/svc.c=844=svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
--
net/sunrpc/svc.c-847- set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
net/sunrpc/svc.c:848: set_bit(SP_NEED_VICTIM, &pool->sp_flags);
net/sunrpc/svc.c-849- svc_pool_wake_idle_thread(pool);
]
|
| 60/2 |
2026/06/01 06:21 |
llm |
expert |
0m
Model:
Tokens:
input: 128672
output: 38
thoughts: 13
Thoughts: Here's a summary of my thought process, as requested:
**Assessing the `svc_thread_should_stop` Flag**
Okay, first things first, let's zero in on this `svc_thread_should_stop` variable. I need to understand its current state. Is it currently set? If so, what initiated that setting? Is it a signal, a specific condition met, or a general shutdown request? Understanding the context here is critical before I make any decisions about the service thread's execution path. I need to make sure I understand the full ramifications of acting on this flag. What other processes are depending on this thread? Where is this flag checked? What are the potential impacts of stopping the thread at this very moment, and will that cause any data corruption? I want to make sure I am fully aware of the state of the system before continuing.
|
| 61/2 |
2026/06/01 06:22 |
tool |
read-file |
0m
Args: map[File:include/linux/sunrpc/svc.h FirstLine:331 LineCount:50]
Results: map[Contents: 331: static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
332: {
333: if (test_and_clear_bit(SP_NEED_VICTIM, &rqstp->rq_pool->sp_flags))
334: set_bit(RQ_VICTIM, &rqstp->rq_flags);
335:
336: return test_bit(RQ_VICTIM, &rqstp->rq_flags);
337: }
338:
339: /**
340: * svc_thread_init_status - report whether thread has initialised successfully
341: * @rqstp: the thread in question
342: * @err: errno code
343: *
344: * After performing any initialisation that could fail, and before starting
345: * normal work, each sunrpc svc_thread must call svc_thread_init_status()
346: * with an appropriate error, or zero.
347: *
348: * If zero is passed, the thread is ready and must continue until
349: * svc_thread_should_stop() returns true. If a non-zero error is passed
350: * the call will not return - the thread will exit.
351: */
352: static inline void svc_thread_init_status(struct svc_rqst *rqstp, int err)
353: {
354: store_release_wake_up(&rqstp->rq_err, err);
355: if (err)
356: kthread_exit(1);
357: }
358:
359: struct svc_deferred_req {
360: u32 prot; /* protocol (UDP or TCP) */
361: struct svc_xprt *xprt;
362: struct sockaddr_storage addr; /* where reply must go */
363: size_t addrlen;
364: struct sockaddr_storage daddr; /* where reply must come from */
365: size_t daddrlen;
366: void *xprt_ctxt;
367: struct cache_deferred_req handle;
368: int argslen;
369: __be32 args[];
370: };
371:
372: struct svc_process_info {
373: union {
374: int (*dispatch)(struct svc_rqst *rqstp);
375: struct {
376: unsigned int lovers;
377: unsigned int hivers;
378: } mismatch;
379: };
380: };
]
|
| 62/2 |
2026/06/01 06:22 |
llm |
expert |
0m
Model:
Tokens:
input: 129684
output: 40
thoughts: 30
Thoughts: Here's my thought process, summarized as you requested:
**Deconstructing Thread Termination**
Okay, so I'm trying to wrap my head around this `svc_thread_should_stop` function. The key here is that it's called *by* the thread itself. That's the crucial point to remember. It's not being externally signaled directly in this context. Now I need to trace this function's use to understand *why* and *when* the thread is making this self-check for termination. Where does this occur? Ah, right, that's in `nfsd()`. I need to review what's happening within `nfsd()` to get the full picture of the thread's lifecycle and termination logic. This should give me the answer I need.
|
| 63/2 |
2026/06/01 06:22 |
tool |
read-file |
0m
Args: map[File:fs/nfsd/nfssvc.c FirstLine:883 LineCount:100]
Results: map[Contents: 883: nfsd(void *vrqstp)
884: {
885: struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
886: struct svc_pool *pool = rqstp->rq_pool;
887: struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list);
888: struct net *net = perm_sock->xpt_net;
889: struct nfsd_net *nn = net_generic(net, nfsd_net_id);
890: struct nfsd_thread_local_info ntli = { };
891: bool have_mutex = false;
892:
893: /* At this point, the thread shares current->fs
894: * with the init process. We need to create files with the
895: * umask as defined by the client instead of init's umask.
896: */
897: svc_thread_init_status(rqstp, unshare_fs_struct());
898:
899: current->fs->umask = 0;
900:
901: atomic_inc(&nfsd_th_cnt);
902:
903: set_freezable();
904:
905: /* use dynamic allocation if ntli should ever become large */
906: static_assert(sizeof(struct nfsd_thread_local_info) < 256);
907: rqstp->rq_private = &ntli;
908:
909: /*
910: * The main request loop
911: */
912: while (!svc_thread_should_stop(rqstp)) {
913: switch (svc_recv(rqstp, 5 * HZ)) {
914: case -ETIMEDOUT:
915: /* No work arrived within the timeout window */
916: if (mutex_trylock(&nfsd_mutex)) {
917: if (pool->sp_nrthreads > pool->sp_nrthrmin) {
918: trace_nfsd_dynthread_kill(net, pool);
919: set_bit(RQ_VICTIM, &rqstp->rq_flags);
920: have_mutex = true;
921: } else {
922: mutex_unlock(&nfsd_mutex);
923: }
924: } else {
925: trace_nfsd_dynthread_trylock_fail(net, pool);
926: }
927: break;
928: case -EBUSY:
929: /* No idle threads; consider spawning another */
930: if (pool->sp_nrthreads < pool->sp_nrthrmax) {
931: if (mutex_trylock(&nfsd_mutex)) {
932: if (pool->sp_nrthreads < pool->sp_nrthrmax) {
933: int ret;
934:
935: trace_nfsd_dynthread_start(net, pool);
936: ret = svc_new_thread(rqstp->rq_server, pool);
937: if (ret)
938: pr_notice_ratelimited("%s: unable to spawn new thread: %d\n",
939: __func__, ret);
940: }
941: mutex_unlock(&nfsd_mutex);
942: } else {
943: trace_nfsd_dynthread_trylock_fail(net, pool);
944: }
945: }
946: clear_bit(SP_TASK_STARTING, &pool->sp_flags);
947: break;
948: default:
949: break;
950: }
951: nfsd_file_net_dispose(nn);
952: }
953:
954: atomic_dec(&nfsd_th_cnt);
955:
956: /* Release the thread */
957: svc_exit_thread(rqstp);
958: if (have_mutex)
959: mutex_unlock(&nfsd_mutex);
960: return 0;
961: }
962:
963: /**
964: * nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request
965: * @rqstp: incoming request
966: *
967: * This RPC dispatcher integrates the NFS server's duplicate reply cache.
968: *
969: * Return values:
970: * %0: Processing complete; do not send a Reply
971: * %1: Processing complete; send Reply in rqstp->rq_res
972: */
973: int nfsd_dispatch(struct svc_rqst *rqstp)
974: {
975: struct nfsd_thread_local_info *ntli = rqstp->rq_private;
976: const struct svc_procedure *proc = rqstp->rq_procinfo;
977: __be32 *statp = rqstp->rq_accept_statp;
978: struct nfsd_cacherep *rp;
979: unsigned int start, len;
980: __be32 *nfs_reply;
981:
982: /*
]
|
| 64/2 |
2026/06/01 06:22 |
llm |
expert |
0m
Model:
Tokens:
input: 131507
output: 17
thoughts: 484
Thoughts: **Analyzing a Potential NFS Server Thread Issue**
Okay, let me think this through. I see the `nfsd` thread calls `svc_thread_init_status` with the result of `unshare_fs_struct()`. And, oh dear, if `unshare_fs_struct()` fails, that thread immediately calls `kthread_exit(1)`. Right, let's look at `svc_thread_init_status`:
```c
static inline void svc_thread_init_status(struct svc_rqst *rqstp, int err)
{
store_release_wake_up(&rqstp->rq_err, err);
if (err)
kthread_exit(1);
}
```
So it's a direct exit via `kthread_exit` if an error occurs. But, crucially, that means it *doesn't* call `svc_exit_thread(rqstp)`! Which means if there is a failure here, `SP_VICTIM_REMAINS` *is* never cleared. That's a problem, because `svc_stop_kthreads` sets `SP_VICTIM_REMAINS` and *waits* for it to be cleared. But `svc_stop_kthreads` is used to shut down an existing thread. If the thread is already running, it *should* have already passed `svc_thread_init_status`.
Aha! What if `svc_new_thread` is called and creates a new thread, but then `unshare_fs_struct()` fails? Let's check. `svc_new_thread` waits for `rqstp->rq_err != -EAGAIN`. And if it fails, it calls `svc_exit_thread(rqstp)`!
```c
/* Wait for the thread to signal initialization status */
wait_var_event(&rqstp->rq_err, rqstp->rq_err != -EAGAIN);
err = rqstp->rq_err;
out:
if (err)
svc_exit_thread(rqstp);
return err;
```
Good, it handles the failure case and cleans up.
But wait a minute, the issue isn't quite resolved yet. If `svc_stop_kthreads` sets `SP_VICTIM_REMAINS` and wakes up an *idle* thread, and that thread is stuck in `svc_recv`, what happens? Okay, better look at `svc_recv`...
|
| 65/2 |
2026/06/01 06:22 |
tool |
grepper |
0m
Args:
Results: map[Output:arch/sparc/include/asm/hypervisor.h=1243=unsigned long sun4v_mach_set_soft_state(unsigned long soft_state,
--
arch/sparc/include/asm/hypervisor.h-1278-
arch/sparc/include/asm/hypervisor.h:1279:/* svc_recv()
arch/sparc/include/asm/hypervisor.h-1280- * TRAP: HV_FAST_TRAP
--
arch/sparc/include/asm/hypervisor.h=1322=unsigned long sun4v_svc_send(unsigned long svc_id,
--
arch/sparc/include/asm/hypervisor.h-1325- unsigned long *sent_bytes);
arch/sparc/include/asm/hypervisor.h:1326:unsigned long sun4v_svc_recv(unsigned long svc_id,
arch/sparc/include/asm/hypervisor.h-1327- unsigned long buffer,
--
arch/sparc/kernel/hvcalls.S=719=ENDPROC(sun4v_svc_send)
arch/sparc/kernel/hvcalls.S-720-
arch/sparc/kernel/hvcalls.S:721:ENTRY(sun4v_svc_recv)
arch/sparc/kernel/hvcalls.S-722- save %sp, -192, %sp
--
arch/sparc/kernel/hvcalls.S-730- restore
arch/sparc/kernel/hvcalls.S:731:ENDPROC(sun4v_svc_recv)
arch/sparc/kernel/hvcalls.S-732-
--
fs/lockd/svc.c=124=lockd(void *vrqstp)
--
fs/lockd/svc.c-131-
fs/lockd/svc.c:132: /* try_to_freeze() is called from svc_recv() */
fs/lockd/svc.c-133- set_freezable();
--
fs/lockd/svc.c-142- nlmsvc_retry_blocked(rqstp);
fs/lockd/svc.c:143: svc_recv(rqstp, 0);
fs/lockd/svc.c-144- }
--
fs/nfs/callback.c=75=nfs4_callback_svc(void *vrqstp)
--
fs/nfs/callback.c-83- while (!svc_thread_should_stop(rqstp))
fs/nfs/callback.c:84: svc_recv(rqstp, 0);
fs/nfs/callback.c-85-
--
fs/nfsd/nfssvc.c=883=nfsd(void *vrqstp)
--
fs/nfsd/nfssvc.c-912- while (!svc_thread_should_stop(rqstp)) {
fs/nfsd/nfssvc.c:913: switch (svc_recv(rqstp, 5 * HZ)) {
fs/nfsd/nfssvc.c-914- case -ETIMEDOUT:
--
include/linux/sunrpc/svcsock.h=56=static inline u32 svc_sock_final_rec(struct svc_sock *svsk)
--
include/linux/sunrpc/svcsock.h-63- */
include/linux/sunrpc/svcsock.h:64:int svc_recv(struct svc_rqst *rqstp, long timeo);
include/linux/sunrpc/svcsock.h-65-void svc_send(struct svc_rqst *rqstp);
--
net/sunrpc/svc_xprt.c=861=static void svc_thread_wake_next(struct svc_rqst *rqstp)
--
net/sunrpc/svc_xprt.c-870-/**
net/sunrpc/svc_xprt.c:871: * svc_recv - Receive and process the next request on any transport
net/sunrpc/svc_xprt.c-872- * @rqstp: an idle RPC service thread
--
net/sunrpc/svc_xprt.c-884- */
net/sunrpc/svc_xprt.c:885:int svc_recv(struct svc_rqst *rqstp, long timeo)
net/sunrpc/svc_xprt.c-886-{
--
net/sunrpc/svc_xprt.c-951-}
net/sunrpc/svc_xprt.c:952:EXPORT_SYMBOL_GPL(svc_recv);
net/sunrpc/svc_xprt.c-953-
--
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c-47- * The main entry point is svc_rdma_recvfrom. This is called from
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c:48: * svc_recv when the transport indicates there is incoming data to
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c-49- * be read. "Data Ready" is signaled when an RDMA Receive completes,
--
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c-68- * When the Read chunk payloads have become available on the
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c:69: * server, "Data Ready" is raised again, and svc_recv calls
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c-70- * svc_rdma_recvfrom again. This second call may use a different
]
|
| 66/2 |
2026/06/01 06:22 |
llm |
expert |
1m
Model:
Tokens:
input: 134005
output: 40
thoughts: 24
Thoughts: Okay, here's my interpretation of what's going through my mind as I approach this code:
**Diving into `svc_recv`**
Right, let's get into the nitty-gritty. I'm going to pull up `svc_recv` in `net/sunrpc/svc_xprt.c`. That's where the magic happens, or at least where the initial reception of RPC calls is handled. My primary interest is understanding the flow of data coming in – how a client's request hits the server. I want to see how the kernel deals with incoming network packets destined for an RPC service. Things I'll be looking out for are: how the data is handled, any potential buffering strategies, how it's linked into other data structures and the interactions with the network interface layers. I'm expecting to find core handling for the request, decoding, and likely some initial checks. Let's see what the developers decided to put here... Time to get started.
|
| 67/2 |
2026/06/01 06:23 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc_xprt.c FirstLine:885 LineCount:50]
Results: map[Contents: 885: int svc_recv(struct svc_rqst *rqstp, long timeo)
886: {
887: struct svc_pool *pool = rqstp->rq_pool;
888: bool did_timeout;
889: int ret = 0;
890:
891: if (!svc_alloc_arg(rqstp))
892: return ret;
893:
894: did_timeout = svc_thread_wait_for_work(rqstp, timeo);
895:
896: if (did_timeout && svc_thread_should_sleep(rqstp) &&
897: pool->sp_nrthrmin && pool->sp_nrthreads > pool->sp_nrthrmin)
898: ret = -ETIMEDOUT;
899:
900: clear_bit(SP_TASK_PENDING, &pool->sp_flags);
901:
902: if (svc_thread_should_stop(rqstp)) {
903: svc_thread_wake_next(rqstp);
904: return ret;
905: }
906:
907: rqstp->rq_xprt = svc_xprt_dequeue(pool);
908: if (rqstp->rq_xprt) {
909: struct svc_xprt *xprt = rqstp->rq_xprt;
910:
911: svc_thread_wake_next(rqstp);
912: /* Normally we will wait up to 5 seconds for any required
913: * cache information to be provided. When there are no
914: * idle threads, we reduce the wait time.
915: */
916: if (pool->sp_idle_threads.first) {
917: rqstp->rq_chandle.thread_wait = 5 * HZ;
918: } else {
919: rqstp->rq_chandle.thread_wait = 1 * HZ;
920: /*
921: * No idle threads: signal -EBUSY so the caller
922: * can consider spawning another thread. Use
923: * SP_TASK_STARTING to limit this signal to one
924: * thread at a time; the caller clears this flag
925: * after starting a new thread.
926: */
927: if (!did_timeout && timeo &&
928: !test_and_set_bit(SP_TASK_STARTING,
929: &pool->sp_flags))
930: ret = -EBUSY;
931: }
932:
933: trace_svc_xprt_dequeue(rqstp);
934: svc_handle_xprt(rqstp, xprt);
]
|
| 68/2 |
2026/06/01 06:23 |
llm |
expert |
0m
Model:
Tokens:
input: 135095
output: 23
thoughts: 32
Thoughts: Here's my analysis of the provided snippet, framed as my own thought process:
**Diving into the `svc_thread_wait_for_work` Call**
Okay, the code is hitting a call to `svc_thread_wait_for_work`. That's interesting. I need to understand what's going on at this point. Time to jump into the definition of `svc_thread_wait_for_work` and see what it's doing. This is likely a critical section of code, possibly related to thread synchronization or queuing work. I'll focus on how this wait interacts with other parts of the system and, depending on the architecture, any potential issues that may arise with the specific implementation, which I will analyse in detail. Understanding its role is key to understanding the behaviour of this section of code.
|
| 69/2 |
2026/06/01 06:23 |
tool |
grepper |
0m
Args: map[Expression:svc_thread_wait_for_work]
Results: map[Output:net/sunrpc/svc_xprt.c=745=static bool svc_schedule_timeout(long timeo)
--
net/sunrpc/svc_xprt.c-749-
net/sunrpc/svc_xprt.c:750:static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo)
net/sunrpc/svc_xprt.c-751-{
--
net/sunrpc/svc_xprt.c=885=int svc_recv(struct svc_rqst *rqstp, long timeo)
--
net/sunrpc/svc_xprt.c-893-
net/sunrpc/svc_xprt.c:894: did_timeout = svc_thread_wait_for_work(rqstp, timeo);
net/sunrpc/svc_xprt.c-895-
]
|
| 70/2 |
2026/06/01 06:23 |
llm |
expert |
0m
Model:
Tokens:
input: 135563
output: 40
thoughts: 30
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already quite familiar with network RPC and kernel internals:
**Diving into svc\_thread\_wait\_for\_work**
Alright, my internal radar is pinging; `svc_thread_wait_for_work` in `net/sunrpc/svc_xprt.c` has caught my attention. I immediately want to dig into that specific function. Clearly, it's a critical piece of the sunrpc service infrastructure. This suggests some kind of thread management or work-queueing system. I need to understand how these service threads are blocked and woken up, waiting for incoming RPC requests. It is a fundamental mechanism of the RPC server. Let's see how that routine does its work, the specifics are key.
|
| 71/2 |
2026/06/01 06:23 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc_xprt.c FirstLine:750 LineCount:50]
Results: map[Contents: 750: static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo)
751: {
752: struct svc_pool *pool = rqstp->rq_pool;
753: bool did_timeout = false;
754:
755: if (svc_thread_should_sleep(rqstp)) {
756: set_current_state(TASK_IDLE | TASK_FREEZABLE);
757: llist_add(&rqstp->rq_idle, &pool->sp_idle_threads);
758: if (likely(svc_thread_should_sleep(rqstp)))
759: did_timeout = svc_schedule_timeout(timeo);
760:
761: while (!llist_del_first_this(&pool->sp_idle_threads,
762: &rqstp->rq_idle)) {
763: /* Work just became available. This thread can only
764: * handle it after removing rqstp from the idle
765: * list. If that attempt failed, some other thread
766: * must have queued itself after finding no
767: * work to do, so that thread has taken responsibly
768: * for this new work. This thread can safely sleep
769: * until woken again.
770: */
771: did_timeout = svc_schedule_timeout(timeo);
772: set_current_state(TASK_IDLE | TASK_FREEZABLE);
773: }
774: __set_current_state(TASK_RUNNING);
775: } else {
776: cond_resched();
777: }
778: try_to_freeze();
779: return did_timeout;
780: }
781:
782: static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt)
783: {
784: spin_lock_bh(&serv->sv_lock);
785: set_bit(XPT_TEMP, &newxpt->xpt_flags);
786: list_add(&newxpt->xpt_list, &serv->sv_tempsocks);
787: serv->sv_tmpcnt++;
788: if (serv->sv_temptimer.function == NULL) {
789: /* setup timer to age temp transports */
790: serv->sv_temptimer.function = svc_age_temp_xprts;
791: mod_timer(&serv->sv_temptimer,
792: jiffies + svc_conn_age_period * HZ);
793: }
794: spin_unlock_bh(&serv->sv_lock);
795: svc_xprt_received(newxpt);
796: }
797:
798: static void svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
799: {
]
|
| 72/2 |
2026/06/01 06:23 |
llm |
expert |
0m
Model:
Tokens:
input: 136673
output: 22
thoughts: 232
Thoughts: **Understanding the Thread Wake-up Mechanism**
Okay, so I'm digging into how these service threads are managed. My initial thought was that `svc_thread_wait_for_work` adds the thread to `pool->sp_idle_threads` and then puts it to sleep. And then, when `svc_pool_wake_idle_thread` is called, it *removes* the thread from that idle list, right? But wait a minute... no, that's not quite it. The code snippet clearly shows `svc_pool_wake_idle_thread` *doesn't* actually remove the thread. It just grabs the first entry, which is the request pointer, and wakes up the process using `wake_up_process()`.
So, the thread wakes up, and what happens next is the critical part. It then has to call `llist_del_first_this`. If that succeeds, it breaks out of its loop and gets to work, meaning it successfully removed itself from the idle list. If it *fails* that means something else got there first, it re-enters the loop and goes back to sleep. Hmmm... interesting. It's an atomic operation to remove it...
But wait a minute, `llist_del_first_this`... that's a custom function. I need to understand that. I need to go and check out exactly what `llist_del_first_this` does, to be absolutely sure.
|
| 73/2 |
2026/06/01 06:23 |
tool |
grepper |
0m
Args: map[Expression:llist_del_first_this]
Results: map[Output:include/linux/llist.h=303=static inline struct llist_node *llist_del_first_init(struct llist_head *head)
--
include/linux/llist.h-311-
include/linux/llist.h:312:extern bool llist_del_first_this(struct llist_head *head,
include/linux/llist.h-313- struct llist_node *this);
--
lib/llist.c=44=EXPORT_SYMBOL_GPL(llist_del_first);
--
lib/llist.c-46-/**
lib/llist.c:47: * llist_del_first_this - delete given entry of lock-less list if it is first
lib/llist.c-48- * @head: the head for your lock-less list
--
lib/llist.c-56- */
lib/llist.c:57:bool llist_del_first_this(struct llist_head *head,
lib/llist.c-58- struct llist_node *this)
--
lib/llist.c-71-}
lib/llist.c:72:EXPORT_SYMBOL_GPL(llist_del_first_this);
lib/llist.c-73-
--
net/sunrpc/svc_xprt.c=750=static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo)
--
net/sunrpc/svc_xprt.c-760-
net/sunrpc/svc_xprt.c:761: while (!llist_del_first_this(&pool->sp_idle_threads,
net/sunrpc/svc_xprt.c-762- &rqstp->rq_idle)) {
]
|
| 74/2 |
2026/06/01 06:23 |
llm |
expert |
0m
Model:
Tokens:
input: 137709
output: 38
thoughts: 791
Thoughts: **Analyzing a Potential Deadlock in the Thread Pool**
Okay, so I'm digging into this code and I think I've spotted a serious issue. `llist_del_first_this` is only going to delete the entry if it's the *actual* first entry in the list, right? If it's not the first, it returns `false`, and the thread goes back to sleep. That's the core of the problem.
Now, consider this: `svc_pool_wake_idle_thread` wakes up *the first* thread. If it's called just once, the first thread wakes up, successfully calls `llist_del_first_this`, and moves on – that’s fine. But, what if `svc_pool_wake_idle_thread` gets called more than once? It wakes up the *same* first thread multiple times, which, by itself, seems harmless.
Here's where it gets interesting: what if the *first* thread is woken up, but *before* it gets to call `llist_del_first_this`, another thread is added to the idle list? If that happens, the *new* thread becomes the first! So, the original thread then calls `llist_del_first_this` but gets a `false` return because it's no longer the first! Back to sleep it goes! But, critically, the *new* first thread isn't woken up by `svc_pool_wake_idle_thread` because that's already been called.
Now, let's look at `svc_stop_kthreads`: this is where things could go really bad. It sets `SP_VICTIM_REMAINS` and `SP_NEED_VICTIM` and then calls `svc_pool_wake_idle_thread` to wake a thread. It then waits on `SP_VICTIM_REMAINS` to be cleared. The awakened thread should clear this bit, right? If it successfully calls `llist_del_first_this`, it proceeds, and *should* clear the flag. However, if the first thread wakes up, but another thread was just added to the idle list, the first thread will fail, go back to sleep. The *new* first thread never gets woken up! So, `SP_VICTIM_REMAINS` is never cleared! And `svc_stop_kthreads` waits… forever. This is a deadlock.
Let me trace the scenario: Thread A is idle, at the head of `sp_idle_threads`. `svc_stop_kthreads` sets the flags and wakes Thread A. Thread B finishes its work, calls `svc_thread_wait_for_work` and adds itself to the idle list, becoming the new first thread! Thread A calls `llist_del_first_this` and fails since it's no longer the first. Thread A goes back to sleep. Thread B is also asleep. `svc_stop_kthreads` is waiting on `SP_VICTIM_REMAINS` and no one wakes up Thread B! That's the deadlock. The core issue is `llist_del_first_this`'s semantics combined with the potential for list modifications between the wake-up and the deletion attempt.
I need to quickly confirm that `svc_thread_should_sleep` doesn't check the victim flags before sleeping to confirm my theory. Otherwise, this whole trace is not valid.
|
| 75/2 |
2026/06/01 06:23 |
tool |
read-file |
0m
Args: map[File:include/linux/sunrpc/svc.h FirstLine:312 LineCount:20]
Results: map[Contents: 312: static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
313: {
314: return (struct sockaddr *) &rqst->rq_daddr;
315: }
316:
317: /**
318: * svc_thread_should_stop - check if this thread should stop
319: * @rqstp: the thread that might need to stop
320: *
321: * To stop an svc thread, the pool flags SP_NEED_VICTIM and SP_VICTIM_REMAINS
322: * are set. The first thread which sees SP_NEED_VICTIM clears it, becoming
323: * the victim using this function. It should then promptly call
324: * svc_exit_thread() to complete the process, clearing SP_VICTIM_REMAINS
325: * so the task waiting for a thread to exit can wake and continue.
326: *
327: * Return values:
328: * %true: caller should invoke svc_exit_thread()
329: * %false: caller should do nothing
330: */
331: static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
]
|
| 76/2 |
2026/06/01 06:23 |
llm |
expert |
0m
Model:
Tokens:
input: 139655
output: 21
thoughts: 18
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Identifying the Sleep Condition**
Hold on a second... I need to be absolutely sure about which flag or condition determines the thread's sleep state. Let me review... Ah, yes, `svc_thread_should_sleep`! That's precisely the variable I'm looking for to check if this specific service thread should be put to sleep at this moment. Excellent. Now I can move forward confidently with the next part of the function and implement the logic related to scheduling.
|
| 77/2 |
2026/06/01 06:24 |
tool |
grepper |
0m
Args: map[Expression:svc_thread_should_sleep]
Results: map[Output:net/sunrpc/svc_xprt.c=718=static bool
net/sunrpc/svc_xprt.c:719:svc_thread_should_sleep(struct svc_rqst *rqstp)
net/sunrpc/svc_xprt.c-720-{
--
net/sunrpc/svc_xprt.c=750=static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo)
--
net/sunrpc/svc_xprt.c-754-
net/sunrpc/svc_xprt.c:755: if (svc_thread_should_sleep(rqstp)) {
net/sunrpc/svc_xprt.c-756- set_current_state(TASK_IDLE | TASK_FREEZABLE);
net/sunrpc/svc_xprt.c-757- llist_add(&rqstp->rq_idle, &pool->sp_idle_threads);
net/sunrpc/svc_xprt.c:758: if (likely(svc_thread_should_sleep(rqstp)))
net/sunrpc/svc_xprt.c-759- did_timeout = svc_schedule_timeout(timeo);
--
net/sunrpc/svc_xprt.c=861=static void svc_thread_wake_next(struct svc_rqst *rqstp)
net/sunrpc/svc_xprt.c-862-{
net/sunrpc/svc_xprt.c:863: if (!svc_thread_should_sleep(rqstp))
net/sunrpc/svc_xprt.c-864- /* More work pending after I dequeued some,
--
net/sunrpc/svc_xprt.c=885=int svc_recv(struct svc_rqst *rqstp, long timeo)
--
net/sunrpc/svc_xprt.c-895-
net/sunrpc/svc_xprt.c:896: if (did_timeout && svc_thread_should_sleep(rqstp) &&
net/sunrpc/svc_xprt.c-897- pool->sp_nrthrmin && pool->sp_nrthreads > pool->sp_nrthrmin)
]
|
| 78/2 |
2026/06/01 06:24 |
llm |
expert |
0m
Model:
Tokens:
input: 140403
output: 40
thoughts: 13
Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own:
**Analyzing `svc_thread_should_sleep`**
Alright, let's pull up the code for `svc_thread_should_sleep`. I need to understand this function's behavior. I'm assuming it's related to some kind of service thread, perhaps a background process or worker. The name itself suggests a determination of whether the thread should relinquish the CPU, potentially to conserve resources or avoid unnecessary busy-waiting. I need to figure out the conditions that trigger this 'sleep' decision. Are we talking about waiting for external events? Resource contention? Perhaps a simple periodic check to keep from hogging the processor? I'll be looking for logical branches, return values, and any interactions with other system components. I'll need to consider what signals might affect this sleep state. Is there a priority involved? Does it consider different types of events or conditions that could make the thread sleep? What are the implications if it incorrectly determines whether to sleep or stay awake? Let's get to the code and find out.
|
| 79/2 |
2026/06/01 06:24 |
tool |
read-file |
0m
Args: map[File:net/sunrpc/svc_xprt.c FirstLine:718 LineCount:20]
Results: map[Contents: 718: static bool
719: svc_thread_should_sleep(struct svc_rqst *rqstp)
720: {
721: struct svc_pool *pool = rqstp->rq_pool;
722:
723: /* did someone call svc_wake_up? */
724: if (test_bit(SP_TASK_PENDING, &pool->sp_flags))
725: return false;
726:
727: /* was a socket queued? */
728: if (!lwq_empty(&pool->sp_xprts))
729: return false;
730:
731: /* are we shutting down? */
732: if (svc_thread_should_stop(rqstp))
733: return false;
734:
735: #if defined(CONFIG_SUNRPC_BACKCHANNEL)
736: if (svc_is_backchannel(rqstp)) {
737: if (!lwq_empty(&rqstp->rq_server->sv_cb_list))
]
|
| 80/2 |
2026/06/01 06:24 |
llm |
expert |
0m
Model:
Tokens:
input: 141011
output: 21
thoughts: 3469
Thoughts: **Deep Dive into Potential NFS Server Thread Deadlock**
Alright, let's break this down. My initial thought process focused on how `svc_thread_should_sleep` interacts with `svc_stop_kthreads`. It's clear that `SP_NEED_VICTIM` plays a crucial role. If it's set, the thread becomes the victim, clears the flag, and exits. I was initially tracing the scenario, considering the order of events when the victim is identified, especially in relation to the wake-up of other threads.
I considered a scenario where a thread might call `svc_thread_should_sleep` *before* `SP_NEED_VICTIM` is set. This would allow the thread to go to sleep and potentially be woken up again later, leading to a race. I walked through the code, focusing on the interactions between `svc_stop_kthreads`, `svc_pool_wake_idle_thread`, and `svc_thread_wait_for_work`. Then, I also looked at cases where `svc_stop_kthreads` wakes up a thread, but that thread exits without properly clearing the `SP_NEED_VICTIM` bit. This would leave a potential race condition behind, preventing threads from processing requests.
Then, I explored scenarios where `svc_pool_wake_idle_thread` is called but the `sp_idle_threads` list is empty, or the thread has *already* exited. This led me to inspect the thread exit path, specifically `svc_exit_thread`. That's where I found that `pool->sp_nrthreads` is decremented during thread exit, which reinforces my assumption that `pool->sp_nrthreads` always holds the current thread count, and that if it is greater than zero there must be a thread.
The next question was: Could an NFS request itself somehow be causing a deadlock? I identified a few places where `nfsd_mutex` is taken, but none of these lock operations are called by regular NFS request processing. I also verified that the try lock would just return an error instead of blocking a thread. This means a direct deadlock from `nfsd_mutex` isn't likely, although the thread could still be blocked.
The net effect of all of this, is that there must be a thread *somewhere* that has not called exit.
If there are no threads that call exit, the thread count would need to stay high, but the thread *must* decrement the counter, because there's logic to decrement it! That, in turn, suggests a hang. So, I checked for lockdep output for any NFS thread. The lockdep only reports locks that threads *hold*, meaning there's not a lot of useful info there. Then I considered the possibility of a hung thread waiting for something: Disk I/O, memory, or network. While it would trigger the hung task detector, syzkaller was showing us other hanging tasks. So the NFS task was *not* detected. I then recalled that syzkaller truncates those reports.
My current focus is on how `svc_stop_kthreads` works. I focused on the do-while loop to understand how it waits for threads to exit and what conditions would potentially cause that wait to become an infinite loop. It must mean the count of threads never hits 0. I traced the execution flow within the loop, considered potential issues with the wake-up mechanism, and re-examined the code. I came to the conclusion that the problem must lie in *some* thread not calling `svc_exit_thread` because a thread *must* exit by decrementing the count!
|
| 81/2 |
2026/06/01 06:24 |
tool |
grepper |
0m
Error:
Args: map[Expression:sp_nrthreads\+\+]
Results:
|
| 82/2 |
2026/06/01 06:24 |
llm |
expert |
0m
Model:
Tokens:
input: 145287
output: 18
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Checking `sp_nrthreads` Increment**
Okay, let's just make sure I'm on the right track here. My immediate thought is to verify the increment of `sp_nrthreads`. I need to ensure the system is correctly accounting for the thread count changes. Specifically, I want to make sure the increments are being applied accurately in the context of whatever operation or process I'm currently investigating. Are we seeing expected values, or are there any anomalies or unexpected patterns I need to flag? Depending on the context, this could be a straightforward check, or it might be something deeply embedded in a complex parallel processing implementation. Gotta start with the basics though, and ensure the fundamental counter is functioning as expected before diving deeper into potential complexities.
|
| 83/2 |
2026/06/01 06:24 |
tool |
grepper |
0m
Args: map[Expression:sp_nrthreads]
Results: map[Output:fs/nfsd/nfs4proc.c=2127=nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
--
fs/nfsd/nfs4proc.c-2171- if (atomic_inc_return(&nn->pending_async_copies) >
fs/nfsd/nfs4proc.c:2172: (int)rqstp->rq_pool->sp_nrthreads)
fs/nfsd/nfs4proc.c-2173- goto out_dec_async_copy_err;
--
fs/nfsd/nfssvc.c=883=nfsd(void *vrqstp)
--
fs/nfsd/nfssvc.c-916- if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c:917: if (pool->sp_nrthreads > pool->sp_nrthrmin) {
fs/nfsd/nfssvc.c-918- trace_nfsd_dynthread_kill(net, pool);
--
fs/nfsd/nfssvc.c-929- /* No idle threads; consider spawning another */
fs/nfsd/nfssvc.c:930: if (pool->sp_nrthreads < pool->sp_nrthrmax) {
fs/nfsd/nfssvc.c-931- if (mutex_trylock(&nfsd_mutex)) {
fs/nfsd/nfssvc.c:932: if (pool->sp_nrthreads < pool->sp_nrthrmax) {
fs/nfsd/nfssvc.c-933- int ret;
--
fs/nfsd/trace.h=94=DECLARE_EVENT_CLASS(nfsd_dynthread_class,
--
fs/nfsd/trace.h-109- __entry->pool_id = pool->sp_id;
fs/nfsd/trace.h:110: __entry->nrthreads = pool->sp_nrthreads;
fs/nfsd/trace.h-111- __entry->nrthrmin = pool->sp_nrthrmin;
--
include/linux/sunrpc/svc.h=36=struct svc_pool {
include/linux/sunrpc/svc.h-37- unsigned int sp_id; /* pool id; also node id on NUMA */
include/linux/sunrpc/svc.h:38: unsigned int sp_nrthreads; /* # of threads currently running in pool */
include/linux/sunrpc/svc.h-39- unsigned int sp_nrthrmin; /* Min number of threads to run per pool */
--
net/sunrpc/svc.c=701=svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
--
net/sunrpc/svc.c-737- serv->sv_nrthreads += 1;
net/sunrpc/svc.c:738: pool->sp_nrthreads += 1;
net/sunrpc/svc.c-739-
--
net/sunrpc/svc.c=876=svc_set_pool_threads(struct svc_serv *serv, struct svc_pool *pool,
--
net/sunrpc/svc.c-895- if (min_threads) {
net/sunrpc/svc.c:896: if (pool->sp_nrthreads > max_threads)
net/sunrpc/svc.c-897- delta = max_threads;
net/sunrpc/svc.c:898: else if (pool->sp_nrthreads < min_threads)
net/sunrpc/svc.c-899- delta = min_threads;
--
net/sunrpc/svc.c-905-
net/sunrpc/svc.c:906: delta -= pool->sp_nrthreads;
net/sunrpc/svc.c-907- if (delta > 0)
--
net/sunrpc/svc.c=1027=svc_exit_thread(struct svc_rqst *rqstp)
--
net/sunrpc/svc.c-1033-
net/sunrpc/svc.c:1034: pool->sp_nrthreads -= 1;
net/sunrpc/svc.c-1035- serv->sv_nrthreads -= 1;
--
net/sunrpc/svc_xprt.c=885=int svc_recv(struct svc_rqst *rqstp, long timeo)
--
net/sunrpc/svc_xprt.c-896- if (did_timeout && svc_thread_should_sleep(rqstp) &&
net/sunrpc/svc_xprt.c:897: pool->sp_nrthrmin && pool->sp_nrthreads > pool->sp_nrthrmin)
net/sunrpc/svc_xprt.c-898- ret = -ETIMEDOUT;
]
|
| 84/2 |
2026/06/01 06:24 |
llm |
expert |
2m
Model:
Tokens:
input: 146773
output: 66
thoughts: 22391
Thoughts: **Deep Dive into a Linux NFS Server Deadlock**
Alright, let's break this down. My initial thought process focused on the thread management within the NFS server, specifically how threads are created, managed, and stopped, and I saw a potential issue. I honed in on `svc_prepare_thread`, `svc_new_thread`, and `svc_exit_thread`. My initial thought was that if `kthread_create_on_node` failed, `svc_exit_thread` would decrement the thread count (`sp_nrthreads`) and everything would be fine. But what if it *hung*?
The first thing I had to consider was what would happen if `kthread_create_on_node` hangs. I followed the code down the rabbit hole, and the implications of `sp_nrthreads` becoming incremented but the thread never truly starting to run quickly became apparent. So, then you have `svc_stop_kthreads` attempting to shut things down. It checks `sp_nrthreads` but it relies on `SP_VICTIM_REMAINS` to be cleared by the exiting thread. If the thread is stuck during the creation, this flag would never be cleared! That's a classic deadlock scenario brewing.
Then, there were a bunch of questions:
* How is `svc_stop_kthreads` invoked?
* Where would threads realistically get stuck?
I had to trace through the call stack: `svc_stop_kthreads` called by `svc_set_pool_threads` which is called by `nfsd_set_nrthreads`, which is *only* called *once* by `nfsd_svc`. Then, I had a thought – if `svc_new_thread` hangs, `nfsd_svc` hangs, and `svc_stop_kthreads` would never be called, right? This seemed safe, because that would never lead to `nfsd_mutex` being blocked, as it's meant to protect, right?
But then I realized, what if `svc_stop_kthreads` is called, and *then* we have a problem? The focus shifted to `svc_recv`, and the possible reasons why a thread might get stuck. If a thread is stuck, `svc_stop_kthreads` *does* get called. I considered that a thread might be stuck in `svc_recv`, inside of `svc_thread_wait_for_work`, sleeping after calling `svc_thread_wait_for_work`.
I went deep into `svc_thread_wait_for_work` and the `llist_del_first_this` routine and thought, what if there's a race with `svc_stop_kthreads` waking up another thread. What would happen with `SP_NEED_VICTIM` being set? I started to see how a thread could get stuck going back to sleep, leading to a situation where *no one* would clear `SP_VICTIM_REMAINS`. That's a DEADLOCK. But I had to verify the race.
I looked at the code for thread A, which is supposed to wake up and exit, as it calls `svc_thread_wait_for_work`. In `svc_thread_wait_for_work`, it sets state to `TASK_IDLE` and adds itself to the `sp_idle_threads` linked list. If `svc_stop_kthreads` wakes it up, how does the thread check to exit? It turns out it has to clear `SP_NEED_VICTIM` in `svc_thread_should_sleep` by clearing `SP_NEED_VICTIM` and setting `RQ_VICTIM`. If it then sets this and returns false, it returns from the work function and will exit.
This is where Thread B comes into play. If Thread B becomes the "victim" instead, then it will set `RQ_VICTIM` and be forced to exit, causing the deadlock to break! Wait, what if the race condition causes the deadlock to live forever?
I spent a lot of time analyzing various race conditions, with the specific order of events:
* Whether the idle thread or the working thread runs first.
* The timing of `svc_thread_should_sleep` and clearing `SP_NEED_VICTIM`.
A very specific scenario became clear where I could reproduce the livelock, where the idle thread (thread A), the victim, will go to sleep in `svc_schedule_timeout(timeo)`, wake up, and fail repeatedly to remove itself, sleeping for short periods over and over, while another thread (Thread B) keeps waking up, adding itself to the list. I proved the race condition and described it, verifying with code.
From
|
| 85/2 |
2026/06/01 06:27 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:true VMGuestTrigger:false VMHostTrigger:false]
|
| 86/2 |
2026/06/01 06:27 |
llm |
expert |
0m
Model:
Tokens:
input: 170319
output: 836
thoughts: 436
Thoughts: **My Analysis of the NFSd Denial of Service Bug**
Okay, the results are in, and I have a clear picture of what's happening. My task now is to craft a comprehensive explanation of this bug and the reasoning behind it.
Essentially, we're looking at a denial-of-service vulnerability, specifically a hang, rooted in an infinite loop within the `svc_alloc_arg` function. This happens when `alloc_page` fails, which, of course, is a direct indicator of out-of-memory (OOM) conditions. The `svc_alloc_arg` function, as a retry strategy, implements a sleep for 500 milliseconds and then retries the allocation, only breaking out of this cycle if it is signalled, or `kthread_should_stop()` returns true.
The problem, however, is that the `nfsd` threads have their own signal-blocking mechanism and unique stopping mechanism. They don't use the standard `kthread_should_stop()`. Instead, they depend on flags like `SP_NEED_VICTIM` and the `svc_thread_should_stop(rqstp)` function to know if they're meant to stop. This is a subtle difference, but it's the root of the problem.
Under OOM conditions, an `nfsd` thread gets stuck looping indefinitely in `TASK_INTERRUPTIBLE` inside `svc_alloc_arg` because it is waiting to be signaled, but it will never be, because `svc_alloc_arg` never checks `svc_thread_should_stop(rqstp)`.
Now, imagine a user trying to gracefully shut down the `nfsd` threads, perhaps by sending a netlink message to reduce the number of threads to zero. This triggers `nfsd_nl_threads_set_doit`, which then acquires `nfsd_mutex` and calls `svc_stop_kthreads`. `svc_stop_kthreads` sets that `SP_NEED_VICTIM` flag and waits for the thread to exit. But the thread, now locked in `svc_alloc_arg`, never checks for `SP_NEED_VICTIM`! It is stuck and never exits!
`svc_stop_kthreads` then hangs forever, holding that critical `nfsd_mutex`. This creates a cascading problem. Any subsequent attempts to configure `nfsd`, whether it be starting it, stopping it, or changing any configurations, will block on `nfsd_mutex` in `TASK_UNINTERRUPTIBLE`. Eventually, this will trigger the hung task detector, which is what we see.
And, crucially, this is all accessible from user namespaces because the `nfsd` netlink family is configured with `.netnsok = true`. The `GENL_ADMIN_PERM` permission is mapped to `CAP_NET_ADMIN`, and an unprivileged user can gain these permissions by creating user and network namespaces. So, a user with seemingly limited privileges can exploit this. I need to emphasize this in my response.
|
| 87/1 |
2026/06/01 06:28 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided bug report and kernel source code, here is
the assessment of the security impact:
### Bug Mechanism
The syzkaller report shows multiple tasks blocked for over 143 seconds in
`__mutex_lock_common` while trying to acquire `nfsd_mutex` inside
`nfsd_nl_threads_set_doit`.
The root cause of this hang is a deadlock/livelock involving `nfsd` thread
shutdown and memory allocation failures.
When an `nfsd` thread processes a request, it allocates pages in
`svc_alloc_arg()`. If `alloc_page(GFP_KERNEL)` fails (which is common under
heavy load or OOM conditions generated by syzkaller), the thread sleeps for
500ms in `TASK_INTERRUPTIBLE` and retries indefinitely. It only breaks out of
this loop if `signalled()` or `kthread_should_stop()` is true.
However, `nfsd` threads block most signals and use a custom stopping mechanism
(`SP_NEED_VICTIM` flag checked via `svc_thread_should_stop(rqstp)`), meaning
`kthread_should_stop()` will never return true for them. Thus, the thread loops
forever.
When a user tries to reconfigure or shutdown the `nfsd` server (e.g., by sending
a netlink message to set the number of threads to 0),
`nfsd_nl_threads_set_doit()` acquires `nfsd_mutex` and eventually calls
`svc_stop_kthreads()`. `svc_stop_kthreads()` sets the `SP_NEED_VICTIM` flag and
waits for the `nfsd` threads to exit. Because the victim thread is stuck in the
`svc_alloc_arg()` infinite loop and never checks `svc_thread_should_stop()`, it
never exits.
Consequently, `svc_stop_kthreads()` hangs forever while holding `nfsd_mutex`.
Any subsequent tasks attempting to configure `nfsd` will block on `nfsd_mutex`
in `TASK_UNINTERRUPTIBLE` state, eventually triggering the hung task detector.
### Security Impact Assessment
* **Exploitable:** No.
This is a pure logic bug leading to a livelock/deadlock. It does not involve
memory corruption, use-after-free, or out-of-bounds accesses that could be
leveraged for privilege escalation or arbitrary code execution.
* **Denial Of Service:** Yes.
Triggering this bug causes the `nfsd` subsystem to hang permanently.
Furthermore, tasks blocked in `TASK_UNINTERRUPTIBLE` will trigger the kernel's
hung task panic (if `kernel.hung_task_panic` is enabled, which is common in many
production environments), leading to a complete system crash.
* **Accessible From Unprivileged Processes:** No.
The netlink command `NFSD_CMD_THREADS_SET` requires `GENL_ADMIN_PERM`, which
translates to the `CAP_NET_ADMIN` capability. A standard unprivileged process
does not have this capability.
* **Accessible From User Namespaces:** Yes.
The `nfsd` netlink family is registered with `.netnsok = true`. An unprivileged
user can create a new user namespace and a new network namespace, thereby
gaining `CAP_NET_ADMIN` within that namespace. From there, they can start an
`nfsd` instance, induce memory pressure, and send the netlink message to trigger
the deadlock.
* **VM Guest / Host / Network / Peripheral / Filesystem Triggers:** No.
This bug is strictly related to the local configuration of the NFS server via
netlink sockets and local memory pressure. It is not triggered by processing
remote network traffic, mounting filesystems, or interacting with peripherals/VM
boundaries.]
|