Skip to content

Commit

Permalink
libbpf-tools: Check biolatency tracepoints using BTF
Browse files Browse the repository at this point in the history
The biolatency has a kernel version check to detect changes in the some
block tracepoint format.  But it can be a problem if the old kernel
backported the change.  Then the BPF verifier would reject the program
due to invalid access to the argument.

We can do better using BTF since it has the type information.  Instead
of checking kernel version, it can check the prototype of the tracepoint
function.  For example, block_rq_issue tracepoint has this for recent
kernels:

  typedef void (*btf_trace_block_rq_issue)(void *, struct request *);

Let's check the number of arguments in the prototype - actually it has
one more argument for the context.

Signed-off-by: Namhyung Kim <[email protected]>
  • Loading branch information
namhyung authored and yonghong-song committed Nov 6, 2023
1 parent 0eb0120 commit 952415e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
5 changes: 3 additions & 2 deletions libbpf-tools/biolatency.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const volatile bool targ_queued = false;
const volatile bool targ_ms = false;
const volatile bool filter_dev = false;
const volatile __u32 targ_dev = 0;
const volatile bool targ_single = true;

struct {
__uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
Expand Down Expand Up @@ -76,7 +77,7 @@ static int handle_block_rq_insert(__u64 *ctx)
* from TP_PROTO(struct request_queue *q, struct request *rq)
* to TP_PROTO(struct request *rq)
*/
if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 11, 0))
if (!targ_single)
return trace_rq_start((void *)ctx[1], false);
else
return trace_rq_start((void *)ctx[0], false);
Expand All @@ -89,7 +90,7 @@ static int handle_block_rq_issue(__u64 *ctx)
* from TP_PROTO(struct request_queue *q, struct request *rq)
* to TP_PROTO(struct request *rq)
*/
if (LINUX_KERNEL_VERSION < KERNEL_VERSION(5, 11, 0))
if (!targ_single)
return trace_rq_start((void *)ctx[1], true);
else
return trace_rq_start((void *)ctx[0], true);
Expand Down
35 changes: 35 additions & 0 deletions libbpf-tools/biolatency.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include "blk_types.h"
#include "biolatency.h"
#include "biolatency.skel.h"
Expand Down Expand Up @@ -233,6 +234,39 @@ static int print_log2_hists(struct bpf_map *hists, struct partitions *partitions
return 0;
}

/*
* BTF has a func proto for each tracepoint, let's check it like
* typedef void (*btf_trace_block_rq_issue)(void *, struct request *);
*
* Actually it's a typedef for a pointer to the func proto.
*/
static bool has_block_rq_issue_single_arg(void)
{
const struct btf *btf = btf__load_vmlinux_btf();
const struct btf_type *t1, *t2, *t3;
__u32 type_id;
bool ret = true; // assuming recent kernels

type_id = btf__find_by_name_kind(btf, "btf_trace_block_rq_issue",
BTF_KIND_TYPEDEF);
if ((__s32)type_id < 0)
return ret;

t1 = btf__type_by_id(btf, type_id);
if (t1 == NULL)
return ret;

t2 = btf__type_by_id(btf, t1->type);
if (t2 == NULL || !btf_is_ptr(t2))
return ret;

t3 = btf__type_by_id(btf, t2->type);
if (t3 && btf_is_func_proto(t3))
ret = (btf_vlen(t3) == 2); // ctx + arg

return ret;
}

int main(int argc, char **argv)
{
struct partitions *partitions = NULL;
Expand Down Expand Up @@ -283,6 +317,7 @@ int main(int argc, char **argv)
obj->rodata->targ_ms = env.milliseconds;
obj->rodata->targ_queued = env.queued;
obj->rodata->filter_cg = env.cg;
obj->rodata->targ_single = has_block_rq_issue_single_arg();

if (probe_tp_btf("block_rq_insert")) {
bpf_program__set_autoload(obj->progs.block_rq_insert, false);
Expand Down

0 comments on commit 952415e

Please sign in to comment.