Fix bio* tools tracepoints

The use of TRACEPOINT_PROBE also solves the RHEL alignment issue,
making the RHEL only patch useless.

Resolves: RHEL-99677

Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
This commit is contained in:
Jerome Marchand 2025-07-28 11:33:07 +02:00
parent 64c73a9b3d
commit 3959565119
3 changed files with 432 additions and 65 deletions

View File

@ -1,61 +0,0 @@
From 8a9da1d866dfb69ad1ca59bdc50a799ba2da3a0c Mon Sep 17 00:00:00 2001
From: Jerome Marchand <jmarchan@redhat.com>
Date: Thu, 24 Oct 2024 16:56:14 +0200
Subject: [PATCH] RHEL/Centos: tools: fix alignment in tp_args for bio tools
The padding in tp_args is wrong on RHEL 9 / c9s kernel because of the
kernel commit 6bc27040eb90 ("sched: Add support for lazy preemption")
which added a common field to tracepoints.
Now the dev field is at an offset of 12 bytes as shown by
block_io_start/done format file.
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
---
tools/biolatency.py | 2 +-
tools/biosnoop.py | 2 +-
tools/biotop.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/biolatency.py b/tools/biolatency.py
index 03b48a4c..ac150ea2 100755
--- a/tools/biolatency.py
+++ b/tools/biolatency.py
@@ -88,7 +88,7 @@ typedef struct ext_val {
} ext_val_t;
struct tp_args {
- u64 __unused__;
+ u32 __unused__[3];
dev_t dev;
sector_t sector;
unsigned int nr_sector;
diff --git a/tools/biosnoop.py b/tools/biosnoop.py
index f0fef98b..819e953f 100755
--- a/tools/biosnoop.py
+++ b/tools/biosnoop.py
@@ -66,7 +66,7 @@ struct val_t {
};
struct tp_args {
- u64 __unused__;
+ u32 __unused__[3];
dev_t dev;
sector_t sector;
unsigned int nr_sector;
diff --git a/tools/biotop.py b/tools/biotop.py
index 879c6b8f..4c3a1c9a 100755
--- a/tools/biotop.py
+++ b/tools/biotop.py
@@ -91,7 +91,7 @@ struct val_t {
};
struct tp_args {
- u64 __unused__;
+ u32 __unused__[3];
dev_t dev;
sector_t sector;
unsigned int nr_sector;
--
2.47.0

View File

@ -0,0 +1,425 @@
From cdd2ade85acd05c8c7af17b0802163ad6fcb8ec7 Mon Sep 17 00:00:00 2001
From: Jerome Marchand <jmarchan@redhat.com>
Date: Tue, 15 Jul 2025 02:25:38 +0200
Subject: [PATCH] tools/{biolatency,biosnoop,biotop}: use TRACEPOINT_PROBE()
for tracepoints (#5366)
When support for block_io_* tracepoint was added, it used
attach_tracepoint() since it was closer to the existing code that
attached to kprobes. However, that meant we have to rely on a local
prototype of the args structure and the tool can be broken by a
change of the tracepoints format, which is what happens with kernel
commit aa6ff4eb7c10d ("block: Add ioprio to block_rq tracepoint")
which added a new argument to block tracepoints.
Replace attach_tracepoint() by TRACEPOINT_PROBE() so we don't have to
deal with the tracepoint format changes.
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
---
tools/biolatency.py | 102 +++++++++++++++++++++++---------------------
tools/biosnoop.py | 86 +++++++++++++++++++------------------
tools/biotop.py | 81 ++++++++++++++++++-----------------
3 files changed, 141 insertions(+), 128 deletions(-)
diff --git a/tools/biolatency.py b/tools/biolatency.py
index 3ba1b237..1d3f2d80 100755
--- a/tools/biolatency.py
+++ b/tools/biolatency.py
@@ -87,17 +87,6 @@ typedef struct ext_val {
u64 count;
} ext_val_t;
-struct tp_args {
- u64 __unused__;
- dev_t dev;
- sector_t sector;
- unsigned int nr_sector;
- unsigned int bytes;
- char rwbs[8];
- char comm[16];
- char cmd[];
-};
-
struct start_key {
dev_t dev;
u32 _pad;
@@ -134,16 +123,6 @@ int trace_req_start(struct pt_regs *ctx, struct request *req)
return __trace_req_start(key);
}
-int trace_req_start_tp(struct tp_args *args)
-{
- struct start_key key = {
- .dev = args->dev,
- .sector = args->sector
- };
-
- return __trace_req_start(key);
-}
-
// output
static int __trace_req_done(struct start_key key)
{
@@ -176,8 +155,22 @@ int trace_req_done(struct pt_regs *ctx, struct request *req)
return __trace_req_done(key);
}
+"""
+
+tp_start_text = """
+TRACEPOINT_PROBE(block, START_TP)
+{
+ struct start_key key = {
+ .dev = args->dev,
+ .sector = args->sector
+ };
+
+ return __trace_req_start(key);
+}
+"""
-int trace_req_done_tp(struct tp_args *args)
+tp_done_text = """
+TRACEPOINT_PROBE(block, DONE_TP)
{
struct start_key key = {
.dev = args->dev,
@@ -266,41 +259,54 @@ bpf_text = bpf_text.replace("STORE", store_str)
if args.ebpf:
exit()
-# load BPF program
-b = BPF(text=bpf_text)
+# Which kprobe/tracepoint to attach to.
+# We can attach to two IO start kprobes but only one of the other types.
+kprobe_start = set()
+tp_start = None
+kprobe_done = None
+tp_done = None
+
if args.queued:
- if BPF.tracepoint_exists("block", "block_io_start"):
- b.attach_tracepoint(tp="block:block_io_start", fn_name="trace_req_start_tp")
- elif BPF.get_kprobe_functions(b'__blk_account_io_start'):
- b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_req_start")
+ if BPF.get_kprobe_functions(b'__blk_account_io_start'):
+ kprobe_start.add("__blk_account_io_start")
elif BPF.get_kprobe_functions(b'blk_account_io_start'):
- b.attach_kprobe(event="blk_account_io_start", fn_name="trace_req_start")
+ kprobe_start.add("blk_account_io_start")
+ elif BPF.tracepoint_exists("block", "block_io_start"):
+ tp_start = "block_io_start"
elif BPF.tracepoint_exists("block", "block_bio_queue"):
- b.attach_tracepoint(tp="block:block_bio_queue", fn_name="trace_req_start_tp")
- else:
- if args.flags:
- # Some flags are accessible in the rwbs field (RAHEAD, SYNC and META)
- # but other aren't. Disable the -F option for tracepoint for now.
- print("ERROR: blk_account_io_start probe not available. Can't use -F.")
- exit()
+ tp_start = "block_bio_queue"
+
else:
if BPF.get_kprobe_functions(b'blk_start_request'):
- b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start")
- b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start")
+ kprobe_start.add("blk_start_request")
+ kprobe_start.add("blk_mq_start_request")
-if BPF.tracepoint_exists("block", "block_io_done"):
- b.attach_tracepoint(tp="block:block_io_done", fn_name="trace_req_done_tp")
-elif BPF.get_kprobe_functions(b'__blk_account_io_done'):
- b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_done")
+if BPF.get_kprobe_functions(b'__blk_account_io_done'):
+ kprobe_done = "__blk_account_io_done"
elif BPF.get_kprobe_functions(b'blk_account_io_done'):
- b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_done")
+ kprobe_done = "blk_account_io_done"
+elif BPF.tracepoint_exists("block", "block_io_done"):
+ tp_done = "block_io_done"
elif BPF.tracepoint_exists("block", "block_rq_complete"):
- b.attach_tracepoint(tp="block:block_rq_complete", fn_name="trace_req_done_tp")
-else:
- if args.flags:
- print("ERROR: blk_account_io_done probe not available. Can't use -F.")
- exit()
+ tp_done = "block_rq_complete"
+
+if args.flags and (tp_start or tp_done):
+ # Some flags are accessible in the rwbs field (RAHEAD, SYNC and META)
+ # but other aren't. Disable the -F option for tracepoint for now.
+ print("ERROR: blk_account_io_start/done probes not available. Can't use -F.")
+ exit()
+
+if tp_start:
+ bpf_text += tp_start_text.replace("START_TP", tp_start)
+if tp_done:
+ bpf_text += tp_done_text.replace("DONE_TP", tp_done)
+# load BPF program
+b = BPF(text=bpf_text)
+for i in kprobe_start:
+ b.attach_kprobe(event=i, fn_name="trace_req_start")
+if kprobe_done:
+ b.attach_kprobe(event=kprobe_done, fn_name="trace_req_done")
if not args.json:
print("Tracing block device I/O... Hit Ctrl-C to end.")
diff --git a/tools/biosnoop.py b/tools/biosnoop.py
index 045e1f9d..65df90c3 100755
--- a/tools/biosnoop.py
+++ b/tools/biosnoop.py
@@ -65,17 +65,6 @@ struct val_t {
char name[TASK_COMM_LEN];
};
-struct tp_args {
- u64 __unused__;
- dev_t dev;
- sector_t sector;
- unsigned int nr_sector;
- unsigned int bytes;
- char rwbs[8];
- char comm[16];
- char cmd[];
-};
-
struct hash_key {
dev_t dev;
u32 rwflag;
@@ -182,17 +171,6 @@ int trace_pid_start(struct pt_regs *ctx, struct request *req)
return __trace_pid_start(key);
}
-int trace_pid_start_tp(struct tp_args *args)
-{
- struct hash_key key = {
- .dev = args->dev,
- .rwflag = get_rwflag_tp(args->rwbs),
- .sector = args->sector
- };
-
- return __trace_pid_start(key);
-}
-
// time block I/O
int trace_req_start(struct pt_regs *ctx, struct request *req)
{
@@ -284,8 +262,23 @@ int trace_req_completion(struct pt_regs *ctx, struct request *req)
return __trace_req_completion(ctx, key);
}
+"""
-int trace_req_completion_tp(struct tp_args *args)
+tp_start_text = """
+TRACEPOINT_PROBE(block, block_io_start)
+{
+ struct hash_key key = {
+ .dev = args->dev,
+ .rwflag = get_rwflag_tp(args->rwbs),
+ .sector = args->sector
+ };
+
+ return __trace_pid_start(key);
+}
+"""
+
+tp_done_text = """
+TRACEPOINT_PROBE(block, block_io_done)
{
struct hash_key key = {
.dev = args->dev,
@@ -296,6 +289,7 @@ int trace_req_completion_tp(struct tp_args *args)
return __trace_req_completion(args, key);
}
"""
+
if args.queue:
bpf_text = bpf_text.replace('##QUEUE##', '1')
else:
@@ -329,31 +323,41 @@ int trace_req_completion_tp(struct tp_args *args)
if args.ebpf:
exit()
-# initialize BPF
-b = BPF(text=bpf_text)
if BPF.tracepoint_exists("block", "block_io_start"):
- b.attach_tracepoint(tp="block:block_io_start", fn_name="trace_pid_start_tp")
-elif BPF.get_kprobe_functions(b'__blk_account_io_start'):
- b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_pid_start")
-elif BPF.get_kprobe_functions(b'blk_account_io_start'):
- b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start")
+ bpf_text += tp_start_text
+ tp_start = True
+else:
+ tp_start = False
+
+if BPF.tracepoint_exists("block", "block_io_done"):
+ bpf_text += tp_done_text
+ tp_done = True
else:
- print("ERROR: No found any block io start probe/tp.")
- exit()
+ tp_done = False
+
+# initialize BPF
+b = BPF(text=bpf_text)
+if not tp_start:
+ if BPF.get_kprobe_functions(b'__blk_account_io_start'):
+ b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_pid_start")
+ elif BPF.get_kprobe_functions(b'blk_account_io_start'):
+ b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start")
+ else:
+ print("ERROR: No found any block io start probe/tp.")
+ exit()
if BPF.get_kprobe_functions(b'blk_start_request'):
b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start")
b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start")
-if BPF.tracepoint_exists("block", "block_io_done"):
- b.attach_tracepoint(tp="block:block_io_done", fn_name="trace_req_completion_tp")
-elif BPF.get_kprobe_functions(b'__blk_account_io_done'):
- b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_completion")
-elif BPF.get_kprobe_functions(b'blk_account_io_done'):
- b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_completion")
-else:
- print("ERROR: No found any block io done probe/tp.")
- exit()
+if not tp_done:
+ if BPF.get_kprobe_functions(b'__blk_account_io_done'):
+ b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_completion")
+ elif BPF.get_kprobe_functions(b'blk_account_io_done'):
+ b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_completion")
+ else:
+ print("ERROR: No found any block io done probe/tp.")
+ exit()
# header
diff --git a/tools/biotop.py b/tools/biotop.py
index 480c37a4..4281b742 100755
--- a/tools/biotop.py
+++ b/tools/biotop.py
@@ -90,17 +90,6 @@ struct val_t {
u32 io;
};
-struct tp_args {
- u64 __unused__;
- dev_t dev;
- sector_t sector;
- unsigned int nr_sector;
- unsigned int bytes;
- char rwbs[8];
- char comm[16];
- char cmd[];
-};
-
struct hash_key {
dev_t dev;
u32 _pad;
@@ -143,16 +132,6 @@ int trace_pid_start(struct pt_regs *ctx, struct request *req)
return __trace_pid_start(key);
}
-int trace_pid_start_tp(struct tp_args *args)
-{
- struct hash_key key = {
- .dev = args->dev,
- .sector = args->sector
- };
-
- return __trace_pid_start(key);
-}
-
// time block I/O
int trace_req_start(struct pt_regs *ctx, struct request *req)
{
@@ -246,8 +225,22 @@ int trace_req_completion(struct pt_regs *ctx, struct request *req)
return __trace_req_completion(key);
}
+"""
+
+tp_start_text = """
+TRACEPOINT_PROBE(block, block_io_start)
+{
+ struct hash_key key = {
+ .dev = args->dev,
+ .sector = args->sector
+ };
+
+ return __trace_pid_start(key);
+}
+"""
-int trace_req_completion_tp(struct tp_args *args)
+tp_done_text = """
+TRACEPOINT_PROBE(block, block_io_done)
{
struct hash_key key = {
.dev = args->dev,
@@ -272,30 +265,40 @@ int trace_req_completion_tp(struct tp_args *args)
else:
bpf_text = bpf_text.replace('FILTER_PID', '0')
-b = BPF(text=bpf_text)
if BPF.tracepoint_exists("block", "block_io_start"):
- b.attach_tracepoint(tp="block:block_io_start", fn_name="trace_pid_start_tp")
-elif BPF.get_kprobe_functions(b'__blk_account_io_start'):
- b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_pid_start")
-elif BPF.get_kprobe_functions(b'blk_account_io_start'):
- b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start")
+ bpf_text += tp_start_text
+ tp_start = True
else:
- print("ERROR: No found any block io start probe/tp.")
- exit()
+ tp_start = False
+
+if BPF.tracepoint_exists("block", "block_io_done"):
+ bpf_text += tp_done_text
+ tp_done = True
+else:
+ tp_done = False
+
+b = BPF(text=bpf_text)
+if not tp_start:
+ if BPF.get_kprobe_functions(b'__blk_account_io_start'):
+ b.attach_kprobe(event="__blk_account_io_start", fn_name="trace_pid_start")
+ elif BPF.get_kprobe_functions(b'blk_account_io_start'):
+ b.attach_kprobe(event="blk_account_io_start", fn_name="trace_pid_start")
+ else:
+ print("ERROR: No found any block io start probe/tp.")
+ exit()
if BPF.get_kprobe_functions(b'blk_start_request'):
b.attach_kprobe(event="blk_start_request", fn_name="trace_req_start")
b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_req_start")
-if BPF.tracepoint_exists("block", "block_io_done"):
- b.attach_tracepoint(tp="block:block_io_done", fn_name="trace_req_completion_tp")
-elif BPF.get_kprobe_functions(b'__blk_account_io_done'):
- b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_completion")
-elif BPF.get_kprobe_functions(b'blk_account_io_done'):
- b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_completion")
-else:
- print("ERROR: No found any block io done probe/tp.")
- exit()
+if not tp_done:
+ if BPF.get_kprobe_functions(b'__blk_account_io_done'):
+ b.attach_kprobe(event="__blk_account_io_done", fn_name="trace_req_completion")
+ elif BPF.get_kprobe_functions(b'blk_account_io_done'):
+ b.attach_kprobe(event="blk_account_io_done", fn_name="trace_req_completion")
+ else:
+ print("ERROR: No found any block io done probe/tp.")
+ exit()
# check whether hash table batch ops is supported
htab_batch_ops = True if BPF.kernel_struct_has_field(b'bpf_map_ops',
--
2.50.1

View File

@ -25,15 +25,15 @@
Name: bcc
Version: 0.34.0
Release: 1%{?dist}
Release: 2%{?dist}
Summary: BPF Compiler Collection (BCC)
License: Apache-2.0
URL: https://github.com/iovisor/bcc
Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz
Patch0: %%{name}-%%{version}-Revert-Fix-bashreadline-4903.patch
Patch1: %%{name}-%%{version}-RHEL-Centos-tools-fix-alignment-in-tp_args-for-bio-t.patch
Patch2: %%{name}-%%{version}-tools-biosnoop-Fix-biosnoop-pattern-option-5304.patch
Patch3: %%{name}-%%{version}-clang-Define-DCONFIG_CC_IS_CLANG-in-KBuildHelper-get.patch
Patch1: %%{name}-%%{version}-tools-biosnoop-Fix-biosnoop-pattern-option-5304.patch
Patch2: %%{name}-%%{version}-clang-Define-DCONFIG_CC_IS_CLANG-in-KBuildHelper-get.patch
Patch3: %%{name}-%%{version}-tools-biolatency-biosnoop-biotop-use-TRACEPOINT_PROB.patch
# Arches will be included as upstream support is added and dependencies are
# satisfied in the respective arches
@ -266,6 +266,9 @@ cp -a libbpf-tools/tmp-install/bin/* %{buildroot}/%{_sbindir}/
%endif
%changelog
* Mon Jul 28 2025 Jerome Marchand <jmarchan@redhat.com> - 0.34.0-2
- Fix bio* tools tracepoints (RHEL-99677)
* Mon May 19 2025 Jerome Marchand <jmarchan@redhat.com> - 0.34.0-1
- Rebase to the latest version (RHEL-78921)
- Rebuild with LLVM 20 (RHEL-81772)