1 /* SPDX-License-Identifier: GPL-2.0 */
2 /* Copyright (c) 2020 Wenbo Zhang */
3 #include <vmlinux.h>
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_core_read.h>
6 #include <bpf/bpf_tracing.h>
7 #include "bits.bpf.h"
8 #include "fsslower.h"
9
10 #define MAX_ENTRIES 8192
11
12 const volatile pid_t target_pid = 0;
13 const volatile __u64 min_lat_ns = 0;
14
15 struct data {
16 __u64 ts;
17 loff_t start;
18 loff_t end;
19 struct file *fp;
20 };
21
22 struct {
23 __uint(type, BPF_MAP_TYPE_HASH);
24 __uint(max_entries, MAX_ENTRIES);
25 __type(key, __u32);
26 __type(value, struct data);
27 } starts SEC(".maps");
28
29 struct {
30 __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
31 __uint(key_size, sizeof(__u32));
32 __uint(value_size, sizeof(__u32));
33 } events SEC(".maps");
34
probe_entry(struct file * fp,loff_t start,loff_t end)35 static int probe_entry(struct file *fp, loff_t start, loff_t end)
36 {
37 __u64 pid_tgid = bpf_get_current_pid_tgid();
38 __u32 pid = pid_tgid >> 32;
39 __u32 tid = (__u32)pid_tgid;
40 struct data data;
41
42 if (!fp)
43 return 0;
44
45 if (target_pid && target_pid != pid)
46 return 0;
47
48 data.ts = bpf_ktime_get_ns();
49 data.start = start;
50 data.end = end;
51 data.fp = fp;
52 bpf_map_update_elem(&starts, &tid, &data, BPF_ANY);
53 return 0;
54 }
55
probe_exit(void * ctx,enum fs_file_op op,ssize_t size)56 static int probe_exit(void *ctx, enum fs_file_op op, ssize_t size)
57 {
58 __u64 pid_tgid = bpf_get_current_pid_tgid();
59 __u32 pid = pid_tgid >> 32;
60 __u32 tid = (__u32)pid_tgid;
61 __u64 end_ns, delta_ns;
62 const __u8 *file_name;
63 struct data *datap;
64 struct event event = {};
65 struct dentry *dentry;
66 struct file *fp;
67
68 if (target_pid && target_pid != pid)
69 return 0;
70
71 datap = bpf_map_lookup_elem(&starts, &tid);
72 if (!datap)
73 return 0;
74
75 bpf_map_delete_elem(&starts, &tid);
76
77 end_ns = bpf_ktime_get_ns();
78 delta_ns = end_ns - datap->ts;
79 if (delta_ns <= min_lat_ns)
80 return 0;
81
82 event.delta_us = delta_ns / 1000;
83 event.end_ns = end_ns;
84 event.offset = datap->start;
85 if (op != F_FSYNC)
86 event.size = size;
87 else
88 event.size = datap->end - datap->start;
89 event.pid = pid;
90 event.op = op;
91 fp = datap->fp;
92 dentry = BPF_CORE_READ(fp, f_path.dentry);
93 file_name = BPF_CORE_READ(dentry, d_name.name);
94 bpf_probe_read_kernel_str(&event.file, sizeof(event.file), file_name);
95 bpf_get_current_comm(&event.task, sizeof(event.task));
96 bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
97 return 0;
98 }
99
100 SEC("kprobe/dummy_file_read")
BPF_KPROBE(file_read_entry,struct kiocb * iocb)101 int BPF_KPROBE(file_read_entry, struct kiocb *iocb)
102 {
103 struct file *fp = BPF_CORE_READ(iocb, ki_filp);
104 loff_t start = BPF_CORE_READ(iocb, ki_pos);
105
106 return probe_entry(fp, start, 0);
107 }
108
109 SEC("kretprobe/dummy_file_read")
BPF_KRETPROBE(file_read_exit,ssize_t ret)110 int BPF_KRETPROBE(file_read_exit, ssize_t ret)
111 {
112 return probe_exit(ctx, F_READ, ret);
113 }
114
115 SEC("kprobe/dummy_file_write")
BPF_KPROBE(file_write_entry,struct kiocb * iocb)116 int BPF_KPROBE(file_write_entry, struct kiocb *iocb)
117 {
118 struct file *fp = BPF_CORE_READ(iocb, ki_filp);
119 loff_t start = BPF_CORE_READ(iocb, ki_pos);
120
121 return probe_entry(fp, start, 0);
122 }
123
124 SEC("kretprobe/dummy_file_write")
BPF_KRETPROBE(file_write_exit,ssize_t ret)125 int BPF_KRETPROBE(file_write_exit, ssize_t ret)
126 {
127 return probe_exit(ctx, F_WRITE, ret);
128 }
129
130 SEC("kprobe/dummy_file_open")
BPF_KPROBE(file_open_entry,struct inode * inode,struct file * file)131 int BPF_KPROBE(file_open_entry, struct inode *inode, struct file *file)
132 {
133 return probe_entry(file, 0, 0);
134 }
135
136 SEC("kretprobe/dummy_file_open")
BPF_KRETPROBE(file_open_exit)137 int BPF_KRETPROBE(file_open_exit)
138 {
139 return probe_exit(ctx, F_OPEN, 0);
140 }
141
142 SEC("kprobe/dummy_file_sync")
BPF_KPROBE(file_sync_entry,struct file * file,loff_t start,loff_t end)143 int BPF_KPROBE(file_sync_entry, struct file *file, loff_t start, loff_t end)
144 {
145 return probe_entry(file, start, end);
146 }
147
148 SEC("kretprobe/dummy_file_sync")
BPF_KRETPROBE(file_sync_exit)149 int BPF_KRETPROBE(file_sync_exit)
150 {
151 return probe_exit(ctx, F_FSYNC, 0);
152 }
153
154 SEC("fentry/dummy_file_read")
BPF_PROG(file_read_fentry,struct kiocb * iocb)155 int BPF_PROG(file_read_fentry, struct kiocb *iocb)
156 {
157 struct file *fp = iocb->ki_filp;
158 loff_t start = iocb->ki_pos;
159
160 return probe_entry(fp, start, 0);
161 }
162
163 SEC("fexit/dummy_file_read")
BPF_PROG(file_read_fexit,struct kiocb * iocb,struct iov_iter * to,ssize_t ret)164 int BPF_PROG(file_read_fexit, struct kiocb *iocb, struct iov_iter *to, ssize_t ret)
165 {
166 return probe_exit(ctx, F_READ, ret);
167 }
168
169 SEC("fentry/dummy_file_write")
BPF_PROG(file_write_fentry,struct kiocb * iocb)170 int BPF_PROG(file_write_fentry, struct kiocb *iocb)
171 {
172 struct file *fp = iocb->ki_filp;
173 loff_t start = iocb->ki_pos;
174
175 return probe_entry(fp, start, 0);
176 }
177
178 SEC("fexit/dummy_file_write")
BPF_PROG(file_write_fexit,struct kiocb * iocb,struct iov_iter * from,ssize_t ret)179 int BPF_PROG(file_write_fexit, struct kiocb *iocb, struct iov_iter *from, ssize_t ret)
180 {
181 return probe_exit(ctx, F_WRITE, ret);
182 }
183
184 SEC("fentry/dummy_file_open")
BPF_PROG(file_open_fentry,struct inode * inode,struct file * file)185 int BPF_PROG(file_open_fentry, struct inode *inode, struct file *file)
186 {
187 return probe_entry(file, 0, 0);
188 }
189
190 SEC("fexit/dummy_file_open")
BPF_PROG(file_open_fexit)191 int BPF_PROG(file_open_fexit)
192 {
193 return probe_exit(ctx, F_OPEN, 0);
194 }
195
196 SEC("fentry/dummy_file_sync")
BPF_PROG(file_sync_fentry,struct file * file,loff_t start,loff_t end)197 int BPF_PROG(file_sync_fentry, struct file *file, loff_t start, loff_t end)
198 {
199 return probe_entry(file, start, end);
200 }
201
202 SEC("fexit/dummy_file_sync")
BPF_PROG(file_sync_fexit)203 int BPF_PROG(file_sync_fexit)
204 {
205 return probe_exit(ctx, F_FSYNC, 0);
206 }
207
208 char LICENSE[] SEC("license") = "GPL";
209