diff --git a/kernel/Makefile b/kernel/Makefile index 47a548e0..9c1cbb6b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -48,6 +48,7 @@ NETDATA_APPS= cachestat \ swap \ vfs \ xfs \ + zfs \ # all: $(NETDATA_APPS) diff --git a/kernel/README.md b/kernel/README.md index 476bb97b..ee91c469 100644 --- a/kernel/README.md +++ b/kernel/README.md @@ -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. diff --git a/kernel/zfs_kern.c b/kernel/zfs_kern.c new file mode 100644 index 00000000..2306bf1c --- /dev/null +++ b/kernel/zfs_kern.c @@ -0,0 +1,208 @@ +#define KBUILD_MODNAME "zfs_netdata" +#include +#include +#include + +#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"; +