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
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
/execsnoop
/exitsnoop
/ext4dist
/ext4file
/ext4slower
/f2fsdist
/f2fsslower
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ APPS = \
drsnoop \
execsnoop \
exitsnoop \
ext4file \
filelife \
filetop \
fsdist \
Expand Down
180 changes: 180 additions & 0 deletions libbpf-tools/ext4file.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2026 Samsung Electronics Co., Ltd.
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>

#include "ext4file.h"

volatile __u64 dev_target = 0;
volatile __u64 blocks_per_group = 0;

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000000);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, u32);
__type(value, struct file_info_key);
} ino_name_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000000);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, struct file_info_key);
__type(value, struct file_info_val);
} file_info_map SEC(".maps");

static __always_inline bool str_equal(const char *a, const char *b) {
for (size_t i = 0; i < MAX_FILE_NAME; i++) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is the value of MAX_FILE_NAME? Where is it defined?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The value is 255 which is defined in ext4file.h. I set this value based on the definition of NAME_MAX in the Linux kernel.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is MAX_FILE_NAME the same as NAME_MAX?
Even if the macro names are different, does it get automatically converted or something?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, I found that the NAME_MAX in Linux kernel is set to 255. So I set a new macro MAX_FILE_NAME to 255

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, I found that the NAME_MAX in Linux kernel is set to 255.

OK.

So I set a new macro MAX_FILE_NAME to 255

I cannot find the code that sets the new macro MAX_FILE_NAME. Could you tell me the line number of ext4file.h where it is defined?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sorry, NAME_MAX in ext4file.h should be changed to MAX_FILE_NAME. I have fixed it

if (a[i] == '\0' && b[i] == '\0')
return true;
if (a[i] != b[i])
return false;
}
return true;
}

SEC("fexit/ext4_add_entry")
int BPF_PROG(my_ext4_add_entry, handle_t *handle,
struct dentry *dentry, struct inode *inode)
{
struct inode *pa_inode = dentry->d_parent->d_inode;
dev_t dev_cur = pa_inode->i_sb->s_dev;
u64 ino_id = inode->i_ino;
struct file_info_key fik = {};
struct file_info_val fiv = {};

if (dev_target && dev_target != dev_cur)
return 0;

fik.fk_ino = ino_id;
fik.fk_pa_ino = pa_inode->i_ino;
bpf_probe_read_str(&fik.fk_name,
sizeof(fik.fk_name), dentry->d_name.name);

if (bpf_map_update_elem(&file_info_map, &fik, &fiv, BPF_ANY))
return 0;
if (bpf_map_update_elem(&ino_name_map, &ino_id, &fik, BPF_ANY))
return 0;
return 0;
}

SEC("tp_btf/ext4_unlink_enter")
int BPF_PROG(my_ext4_unlink,
struct inode *pa_inode, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct file_info_key fik = {}, *fikp;
struct file_info_val *fivp = NULL;
u64 ino_id = inode->i_ino;
dev_t dev_cur = pa_inode->i_sb->s_dev;
if (dev_target && dev_target != dev_cur)
return 0;

fik.fk_ino = inode->i_ino;
fik.fk_pa_ino = pa_inode->i_ino;
bpf_probe_read_str(&fik.fk_name,
sizeof(fik.fk_name), dentry->d_name.name);
fivp = bpf_map_lookup_elem(&file_info_map, &fik);
if (!fivp)
return 0;
fivp->fv_delete = true;
fikp = bpf_map_lookup_elem(&ino_name_map, &ino_id);
if (!fikp)
return 0;
if (str_equal(fikp->fk_name, fik.fk_name))
bpf_map_delete_elem(&ino_name_map, &ino_id);
return 0;
}

SEC("fentry/ext4_rmdir")
int BPF_PROG(my_ext4_rmdir,
struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct file_info_key fik = {}, *fikp;
struct file_info_val *fivp = NULL;
u64 ino_id = inode->i_ino;
dev_t dev_cur = inode->i_sb->s_dev;
if (dev_target && dev_target != dev_cur)
return 0;

fik.fk_ino = inode->i_ino;
fik.fk_pa_ino = dir->i_ino;
bpf_probe_read_str(&fik.fk_name,
sizeof(fik.fk_name), dentry->d_name.name);
fivp = bpf_map_lookup_elem(&file_info_map, &fik);
if (!fivp)
return 0;
fivp->fv_delete = true;

fikp = bpf_map_lookup_elem(&ino_name_map, &ino_id);
if (!fikp)
return 0;
if (str_equal(fikp->fk_name, fik.fk_name))
bpf_map_delete_elem(&ino_name_map, &ino_id);
return 0;
}

SEC("fentry/ext4_file_write_iter")
int BPF_PROG(my_ext4_file_write_iter,
struct kiocb *iocb, struct iov_iter *from)
{
struct inode *inode = iocb->ki_filp->f_inode;
dev_t dev_cur = inode->i_sb->s_dev;
struct file_info_key *fikp = NULL;
struct file_info_val *fivp = NULL;
u64 ino_id = inode->i_ino;

if (dev_target && dev_target != dev_cur)
return 0;

fikp = bpf_map_lookup_elem(&ino_name_map, &ino_id);
if (!fikp)
return 0;
fivp = bpf_map_lookup_elem(&file_info_map, fikp);
if (!fivp)
return 0;

fivp->fv_hint = inode->i_write_hint;
if(iocb->ki_flags & IOCB_DIRECT)
if (fivp->fv_rw_cnt[RW_TYPE_DIRECT_WRITE] < (__u64)-1)
__sync_fetch_and_add(&fivp->fv_rw_cnt[RW_TYPE_DIRECT_WRITE], 1);
else
if (fivp->fv_rw_cnt[RW_TYPE_BUFFER_WRITE] < (__u64)-1)
__sync_fetch_and_add(&fivp->fv_rw_cnt[RW_TYPE_BUFFER_WRITE], 1);
return 0;
}

SEC("fentry/ext4_file_read_iter")
int BPF_PROG(my_ext4_file_read_iter,
struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_inode;
dev_t dev_cur = inode->i_sb->s_dev;
struct file_info_key *fikp = NULL;
struct file_info_val *fivp = NULL;
u64 ino_id = inode->i_ino;
if (dev_target && dev_target != dev_cur)
return 0;

fikp = bpf_map_lookup_elem(&ino_name_map, &ino_id);
if (!fikp)
return 0;
fivp = bpf_map_lookup_elem(&file_info_map, fikp);
if (!fivp)
return 0;

if (iocb->ki_flags & IOCB_DIRECT)
if (fivp->fv_rw_cnt[RW_TYPE_DIRECT_READ] < (__u64)-1)
__sync_fetch_and_add(&fivp->fv_rw_cnt[RW_TYPE_DIRECT_READ], 1);
else
if (fivp->fv_rw_cnt[RW_TYPE_BUFFER_READ] < (__u64)-1)
__sync_fetch_and_add(&fivp->fv_rw_cnt[RW_TYPE_BUFFER_READ], 1);
return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";
Loading