// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2022 Francis Laniel #include #include #include #include #include "biotop.h" #include "maps.bpf.h" #include "core_fixes.bpf.h" struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, struct request *); __type(value, struct start_req_t); } start SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, struct request *); __type(value, struct who_t); } whobyreq SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 10240); __type(key, struct info_t); __type(value, struct val_t); } counts SEC(".maps"); static __always_inline int trace_start(struct request *req) { struct who_t who = {}; /* cache PID and comm by-req */ bpf_get_current_comm(&who.name, sizeof(who.name)); who.pid = bpf_get_current_pid_tgid() >> 32; bpf_map_update_elem(&whobyreq, &req, &who, 0); return 0; } SEC("kprobe/blk_mq_start_request") int BPF_KPROBE(blk_mq_start_request, struct request *req) { /* time block I/O */ struct start_req_t start_req; start_req.ts = bpf_ktime_get_ns(); start_req.data_len = BPF_CORE_READ(req, __data_len); bpf_map_update_elem(&start, &req, &start_req, 0); return 0; } static __always_inline int trace_done(struct request *req) { struct val_t *valp, zero = {}; struct info_t info = {}; struct start_req_t *startp; unsigned int cmd_flags; struct gendisk *disk; struct who_t *whop; u64 delta_us; /* fetch timestamp and calculate delta */ startp = bpf_map_lookup_elem(&start, &req); if (!startp) return 0; /* missed tracing issue */ delta_us = (bpf_ktime_get_ns() - startp->ts) / 1000; /* setup info_t key */ cmd_flags = BPF_CORE_READ(req, cmd_flags); disk = get_disk(req); info.major = BPF_CORE_READ(disk, major); info.minor = BPF_CORE_READ(disk, first_minor); info.rwflag = !!((cmd_flags & REQ_OP_MASK) == REQ_OP_WRITE); whop = bpf_map_lookup_elem(&whobyreq, &req); if (whop) { info.pid = whop->pid; __builtin_memcpy(&info.name, whop->name, sizeof(info.name)); } valp = bpf_map_lookup_or_try_init(&counts, &info, &zero); if (valp) { /* save stats */ valp->us += delta_us; valp->bytes += startp->data_len; valp->io++; } bpf_map_delete_elem(&start, &req); bpf_map_delete_elem(&whobyreq, &req); return 0; } SEC("kprobe/blk_account_io_start") int BPF_KPROBE(blk_account_io_start, struct request *req) { return trace_start(req); } SEC("kprobe/blk_account_io_done") int BPF_KPROBE(blk_account_io_done, struct request *req) { return trace_done(req); } SEC("kprobe/__blk_account_io_start") int BPF_KPROBE(__blk_account_io_start, struct request *req) { return trace_start(req); } SEC("kprobe/__blk_account_io_done") int BPF_KPROBE(__blk_account_io_done, struct request *req) { return trace_done(req); } SEC("tp_btf/block_io_start") int BPF_PROG(block_io_start, struct request *req) { return trace_start(req); } SEC("tp_btf/block_io_done") int BPF_PROG(block_io_done, struct request *req) { return trace_done(req); } char LICENSE[] SEC("license") = "GPL";