Skip to content

Commit

Permalink
ZFS monitoring (#241)
Browse files Browse the repository at this point in the history
Add new eBPF program to monitor ZFS filesystem.
  • Loading branch information
thiagoftsm authored Jul 9, 2021
1 parent eec36e1 commit c995f8e
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
1 change: 1 addition & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ NETDATA_APPS= cachestat \
swap \
vfs \
xfs \
zfs \
#

all: $(NETDATA_APPS)
Expand Down
1 change: 1 addition & 0 deletions kernel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ Right now we have two `eBPF` program collections:
- `syncfs_kern.c` : eBPF program that monitor calls for syscall `syncfs`.
- `vfs_kern.c` : eBPF program that monitor Virtual Filesystem functions.
- `xfs_kern.c` : eBPF program that provides XFS monitoring.
- `zfs_kern.c` : eBPF program that provides ZFS monitoring.
208 changes: 208 additions & 0 deletions kernel/zfs_kern.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#define KBUILD_MODNAME "zfs_netdata"
#include <linux/bpf.h>
#include <linux/ptrace.h>
#include <linux/genhd.h>

#include "bpf_helpers.h"
#include "netdata_ebpf.h"

/************************************************************************************
*
* MAP Section
*
***********************************************************************************/

struct bpf_map_def SEC("maps") tbl_zfs = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = NETDATA_FS_MAX_ELEMENTS
};

struct bpf_map_def SEC("maps") tmp_zfs = {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0))
.type = BPF_MAP_TYPE_HASH,
#else
.type = BPF_MAP_TYPE_PERCPU_HASH,
#endif
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 4192
};

/************************************************************************************
*
* ENTRY Section
*
***********************************************************************************/

#if (LINUX_VERSION_CODE > KERNEL_VERSION(5,0,0))
static int netdata_zfs_entry()
#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4,19,0))
static __always_inline int netdata_zfs_entry()
#else
static inline int netdata_zfs_entry()
#endif
{
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = (__u32)(pid_tgid >> 32);
__u64 ts = bpf_ktime_get_ns();

bpf_map_update_elem(&tmp_zfs, &pid, &ts, BPF_ANY);

return 0;
}

SEC("kprobe/zpl_iter_read")
int netdata_zpl_iter_read(struct pt_regs *ctx)
{
return netdata_zfs_entry();
}

SEC("kprobe/zpl_iter_write")
int netdata_zpl_iter_write(struct pt_regs *ctx)
{
return netdata_zfs_entry();
}

SEC("kprobe/zpl_open")
int netdata_zpl_open(struct pt_regs *ctx)
{
return netdata_zfs_entry();
}

SEC("kprobe/zpl_fsync")
int netdata_zpl_fsync(struct pt_regs *ctx)
{
return netdata_zfs_entry();
}

/************************************************************************************
*
* END Section
*
***********************************************************************************/

static void netdata_zfs_store_bin(__u32 bin, __u32 selection)
{
__u64 *fill, data;
__u32 idx = selection * NETDATA_FS_MAX_BINS + bin;
if (idx >= NETDATA_FS_MAX_ELEMENTS)
return;

fill = bpf_map_lookup_elem(&tbl_zfs, &idx);
if (fill) {
libnetdata_update_u64(fill, 1);
return;
}

data = 1;
bpf_map_update_elem(&tbl_zfs, &idx, &data, BPF_ANY);
}

SEC("kretprobe/zpl_iter_read")
int netdata_ret_zpl_iter_read(struct pt_regs *ctx)
{
__u64 *fill, data;
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 bin, pid = (__u32)(pid_tgid >> 32);

fill = bpf_map_lookup_elem(&tmp_zfs, &pid);
if (!fill)
return 0;

data = bpf_ktime_get_ns() - *fill;
bpf_map_delete_elem(&tmp_zfs, &pid);

// Skip entries with backward time
if ( (s64)data < 0)
return 0;

// convert to microseconds
data /= 1000;
bin = libnetdata_select_idx(data, NETDATA_FS_MAX_BINS_POS);
netdata_zfs_store_bin(bin, NETDATA_KEY_CALLS_READ);

return 0;
}

SEC("kretprobe/zpl_iter_write")
int netdata_ret_zpl_iter_write(struct pt_regs *ctx)
{
__u64 *fill, data;
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 bin, pid = (__u32)(pid_tgid >> 32);

fill = bpf_map_lookup_elem(&tmp_zfs, &pid);
if (!fill)
return 0;

data = bpf_ktime_get_ns() - *fill;
bpf_map_delete_elem(&tmp_zfs, &pid);

// Skip entries with backward time
if ( (s64)data < 0)
return 0;

// convert to microseconds
data /= 1000;
bin = libnetdata_select_idx(data, NETDATA_FS_MAX_BINS_POS);
netdata_zfs_store_bin(bin, NETDATA_KEY_CALLS_WRITE);

return 0;
}

SEC("kretprobe/zpl_open")
int netdata_ret_zpl_open(struct pt_regs *ctx)
{
__u64 *fill, data;
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 bin, pid = (__u32)(pid_tgid >> 32);

fill = bpf_map_lookup_elem(&tmp_zfs, &pid);
if (!fill)
return 0;

data = bpf_ktime_get_ns() - *fill;
bpf_map_delete_elem(&tmp_zfs, &pid);

// Skip entries with backward time
if ( (s64)data < 0)
return 0;

// convert to microseconds
data /= 1000;
bin = libnetdata_select_idx(data, NETDATA_FS_MAX_BINS_POS);
netdata_zfs_store_bin(bin, NETDATA_KEY_CALLS_OPEN);

return 0;
}

SEC("kretprobe/zpl_fsync")
int netdata_ret_zpl_fsync(struct pt_regs *ctx)
{
__u64 *fill, data;
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 bin, pid = (__u32)(pid_tgid >> 32);

fill = bpf_map_lookup_elem(&tmp_zfs, &pid);
if (!fill)
return 0;

data = bpf_ktime_get_ns() - *fill;
bpf_map_delete_elem(&tmp_zfs, &pid);

// Skip entries with backward time
if ( (s64)data < 0)
return 0;

// convert to microseconds
data /= 1000;
bin = libnetdata_select_idx(data, NETDATA_FS_MAX_BINS_POS);
netdata_zfs_store_bin(bin, NETDATA_KEY_CALLS_SYNC);

return 0;
}

char _license[] SEC("license") = "GPL";

0 comments on commit c995f8e

Please sign in to comment.