Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 0 additions & 195 deletions libbpf-tools/klockstat.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,201 +832,6 @@ int BPF_KPROBE(kprobe_up_write, struct rw_semaphore *lock)
return 0;
}

/* CONFIG_DEBUG_LOCK_ALLOC is enabled */
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to refactor beyond the immediate fix. Given that this code paths depend on multiple conditions — fentry vs. kprobe, CONFIG_DEBUG_LOCK_ALLOC, and kernel ≥ v6.16 — could you share which combinations you were able to test?


SEC("kprobe/mutex_lock_nested")
int BPF_KPROBE(kprobe_mutex_lock_nested, struct mutex *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/mutex_lock_nested")
int BPF_KRETPROBE(kprobe_mutex_lock_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);
lock_acquired(*lock);
return 0;
}

SEC("kprobe/mutex_lock_interruptible_nested")
int BPF_KPROBE(kprobe_mutex_lock_interruptible_nested, struct mutex *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/mutex_lock_interruptible_nested")
int BPF_KRETPROBE(kprobe_mutex_lock_interruptible_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

if (ret)
lock_aborted(*lock);
else
lock_acquired(*lock);
return 0;
}

SEC("kprobe/mutex_lock_killable_nested")
int BPF_KPROBE(kprobe_mutex_lock_killable_nested, struct mutex *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/mutex_lock_killable_nested")
int BPF_KRETPROBE(kprobe_mutex_lock_killable_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

if (ret)
lock_aborted(*lock);
else
lock_acquired(*lock);
return 0;
}

SEC("kprobe/down_read_nested")
int BPF_KPROBE(kprobe_down_read_nested, struct rw_semaphore *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/down_read_nested")
int BPF_KRETPROBE(kprobe_down_read_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

lock_acquired(*lock);
return 0;
}

SEC("kprobe/down_read_killable_nested")
int BPF_KPROBE(kprobe_down_read_killable_nested, struct rw_semaphore *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/down_read_killable_nested")
int BPF_KRETPROBE(kprobe_down_read_killable_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

if (ret)
lock_aborted(*lock);
else
lock_acquired(*lock);
return 0;
}

SEC("kprobe/down_write_nested")
int BPF_KPROBE(kprobe_down_write_nested, struct rw_semaphore *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/down_write_nested")
int BPF_KRETPROBE(kprobe_down_write_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

lock_acquired(*lock);
return 0;
}

SEC("kprobe/down_write_killable_nested")
int BPF_KPROBE(kprobe_down_write_killable_nested, struct rw_semaphore *lock)
{
u32 tid = (u32)bpf_get_current_pid_tgid();

bpf_map_update_elem(&locks, &tid, &lock, BPF_ANY);
lock_contended(ctx, lock);
return 0;
}

SEC("kretprobe/down_write_killable_nested")
int BPF_KRETPROBE(kprobe_down_write_killable_exit_nested, long ret)
{
u32 tid = (u32)bpf_get_current_pid_tgid();
void **lock;

lock = bpf_map_lookup_elem(&locks, &tid);
if (!lock)
return 0;

bpf_map_delete_elem(&locks, &tid);

if (ret)
lock_aborted(*lock);
else
lock_acquired(*lock);
return 0;
}

SEC("kprobe/rtnetlink_rcv_msg")
int BPF_KPROBE(kprobe_rtnetlink_rcv_msg, struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *ext)
Expand Down
123 changes: 68 additions & 55 deletions libbpf-tools/klockstat.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ static const char *lock_ksym_names[] = {
"mutex_lock_interruptible_nested",
"mutex_lock_killable",
"mutex_lock_killable_nested",
"_mutex_lock_killable",
"mutex_trylock",
"_mutex_trylock_nest_lock",
"down_read",
"down_read_nested",
"down_read_interruptible",
Expand Down Expand Up @@ -775,8 +777,6 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va

static void enable_fentry(struct klockstat_bpf *obj)
{
bool debug_lock;

bpf_program__set_autoload(obj->progs.kprobe_mutex_lock, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_trylock, false);
Expand Down Expand Up @@ -804,22 +804,6 @@ static void enable_fentry(struct klockstat_bpf *obj)
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_up_write, false);

bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_exit_nested, false);

bpf_program__set_autoload(obj->progs.kprobe_down_read_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_exit_nested, false);

bpf_program__set_autoload(obj->progs.kprobe_rtnetlink_rcv_msg, false);
bpf_program__set_autoload(obj->progs.kprobe_rtnetlink_rcv_msg_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_netlink_dump, false);
Expand All @@ -828,8 +812,7 @@ static void enable_fentry(struct klockstat_bpf *obj)
bpf_program__set_autoload(obj->progs.kprobe_sock_do_ioctl_exit, false);

/* CONFIG_DEBUG_LOCK_ALLOC is on */
debug_lock = fentry_can_attach("mutex_lock_nested", NULL);
if (!debug_lock)
if (!fentry_can_attach("mutex_lock_nested", NULL))
return;

bpf_program__set_attach_target(obj->progs.mutex_lock, 0,
Expand All @@ -840,10 +823,6 @@ static void enable_fentry(struct klockstat_bpf *obj)
"mutex_lock_interruptible_nested");
bpf_program__set_attach_target(obj->progs.mutex_lock_interruptible_exit, 0,
"mutex_lock_interruptible_nested");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
"mutex_lock_killable_nested");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
"mutex_lock_killable_nested");

bpf_program__set_attach_target(obj->progs.down_read, 0,
"down_read_nested");
Expand All @@ -861,6 +840,24 @@ static void enable_fentry(struct klockstat_bpf *obj)
"down_write_killable_nested");
bpf_program__set_attach_target(obj->progs.down_write_killable_exit, 0,
"down_write_killable_nested");

/* Since v6.16 mutex_lock_killable nested variant is implemented differently */
if (fentry_can_attach("_mutex_lock_killable", NULL)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_mutex_lock_killable handling is inside DEBUG-only section.
This function exists in all kernels >= 6.16 (not debug-specific) ?
Line 849 may also need to be adjusted depending on line 877.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see it in /proc/kallsyms on a non-debug kernel and both definitions of _mutex_lock_killable(), in mutex.c and rtmutex_api.c are inside a #ifdef CONFIG_DEBUG_LOCK_ALLOC section.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You are right — _mutex_lock_killable handling belongs in the debug section. Thanks for the correction.
  2. The two assignments to mutex_lock_killable / mutex_lock_killable_exit are mutually exclusive alternatives depending on the kernel version, but the code expresses this as an unconditional default followed by a conditional update. An if/else would make the intent clearer.

Thank you.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright. I'll update the pull request soon with your suggestion.

bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
"_mutex_lock_killable");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
"_mutex_lock_killable");
} else {
bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
"mutex_lock_killable_nested");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
"mutex_lock_killable_nested");
}

/* Since v6.16 mutex_trylock also have a nested variant */
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammar: "mutex_trylock also have a nested variant" should be "mutex_trylock also has a nested variant".

Suggested change
/* Since v6.16 mutex_trylock also have a nested variant */
/* Since v6.16 mutex_trylock also has a nested variant */

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I can fix that.

if (fentry_can_attach("_mutex_trylock_nest_lock", NULL))
bpf_program__set_attach_target(obj->progs.mutex_trylock_exit, 0,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike other mutex functions, only the exit probe is retargeted here. A short comment explaining why — e.g., that mutex_trylock is non-blocking so no contention-entry tracking is needed — would help future readers understand the intent.

"_mutex_trylock_nest_lock");
}

static void enable_kprobes(struct klockstat_bpf *obj)
Expand Down Expand Up @@ -897,38 +894,54 @@ static void enable_kprobes(struct klockstat_bpf *obj)
bpf_program__set_autoload(obj->progs.sock_do_ioctl_exit, false);

/* CONFIG_DEBUG_LOCK_ALLOC is on */
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation for this comment uses spaces rather than the tabs used throughout the file, which makes the block look misaligned in the new control flow. Please adjust to match the surrounding indentation style.

Suggested change
/* CONFIG_DEBUG_LOCK_ALLOC is on */
/* CONFIG_DEBUG_LOCK_ALLOC is on */

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not part of my patch, but I can implement that change if you want.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the logic is inverted to an early-return guard, this comment reads more naturally if placed after the guard rather than before it.

if (kprobe_exists("mutex_lock_nested")) {
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_exit, false);

bpf_program__set_autoload(obj->progs.kprobe_down_read, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_exit, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_exit, false);
if (!kprobe_exists("mutex_lock_nested"))
return;

bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock, 0,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this API call have the intended effect for BPF_PROG_TYPE_KPROBE programs on BTF-less kernels with CONFIG_DEBUG_LOCK_ALLOC enabled?

"mutex_lock_nested");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_exit, 0,
"mutex_lock_nested");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_interruptible, 0,
"mutex_lock_interruptible_nested");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_interruptible_exit, 0,
"mutex_lock_interruptible_nested");

bpf_program__set_attach_target(obj->progs.kprobe_down_read, 0,
"down_read_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_read_exit, 0,
"down_read_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_read_killable, 0,
"down_read_killable_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_read_killable_exit, 0,
"down_read_killable_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_write, 0,
"down_write_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_write_exit, 0,
"down_write_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_write_killable, 0,
"down_write_killable_nested");
bpf_program__set_attach_target(obj->progs.kprobe_down_write_killable_exit, 0,
"down_write_killable_nested");

/* Since v6.16 mutex_lock_killable nested variant is implemented differently */
if (kprobe_exists("_mutex_lock_killable")) {
bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kprobe_mutex_lock_killable ?

"_mutex_lock_killable");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
Comment on lines +928 to +930
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In enable_kprobes(), when _mutex_lock_killable exists you’re calling bpf_program__set_attach_target() on the fentry mutex_lock_killable programs, but those were set to autoload=false at the top of enable_kprobes(). This means kprobe_mutex_lock_killable(_exit) will still attach to the old symbol and miss nested killable locks on newer kernels. Please retarget the kprobe mutex_lock_killable entry/exit programs to _mutex_lock_killable instead.

Suggested change
bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
"_mutex_lock_killable");
bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_killable, 0,
"_mutex_lock_killable");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_killable_exit, 0,

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously some sloppy copy and paste business on my part. My bad! Will fix.

"_mutex_lock_killable");
} else {
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_exit_nested, false);

bpf_program__set_autoload(obj->progs.kprobe_down_read_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_exit_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_nested, false);
bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_exit_nested, false);
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_killable, 0,
"mutex_lock_killable_nested");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_lock_killable_exit, 0,
"mutex_lock_killable_nested");
}

/* Since v6.16 mutex_trylock also have a nested variant */
if (kprobe_exists("_mutex_trylock_nest_lock")) {
bpf_program__set_attach_target(obj->progs.kprobe_mutex_trylock, 0,
"_mutex_trylock_nest_lock");
bpf_program__set_attach_target(obj->progs.kprobe_mutex_trylock_exit, 0,
"_mutex_trylock_nest_lock");
}
}

Expand Down
Loading