From 2d63d907d45815485e0064eabbac8e432f65bc17 Mon Sep 17 00:00:00 2001 From: "Mohamed S. Mahmoud" <mmahmoud@redhat.com> Date: Fri, 23 Jun 2023 12:28:29 -0400 Subject: [PATCH] NETOBSERV-1103: optimize ebpf agent map memory and cpu usage (#140) * Optimize ebpf agent map memory usage - switch to use pointer to metric instead of metric - manuall trigger GC after flow eviction complete Signed-off-by: msherif1234 <mmahmoud@redhat.com> * Fix memory and cpu scale issue work around in #133 following up on https://github.com/cilium/ebpf/issues/1063 it seems we have a way to fix resources issues Signed-off-by: msherif1234 <mmahmoud@redhat.com> (cherry picked from commit b9c9a035b25bd5b349071977aff0d7aebca40e75) --------- Signed-off-by: msherif1234 <mmahmoud@redhat.com> --- bpf/flows.c | 1 - bpf/headers/bpf_core_read.h | 484 ++++++++++++++++++++++++++++++++++ pkg/agent/agent.go | 6 +- pkg/agent/agent_test.go | 4 +- pkg/agent/config.go | 2 + pkg/ebpf/bpf_bpfeb.o | Bin 22344 -> 27208 bytes pkg/ebpf/bpf_bpfel.o | Bin 21568 -> 27280 bytes pkg/ebpf/tracer.go | 31 +-- pkg/exporter/ipfix.go | 2 +- pkg/flow/tracer_map.go | 19 +- pkg/flow/tracer_ringbuf.go | 11 +- pkg/test/tracer_fake.go | 10 +- scripts/update-bpf-headers.sh | 1 + 13 files changed, 533 insertions(+), 38 deletions(-) create mode 100644 bpf/headers/bpf_core_read.h diff --git a/bpf/flows.c b/bpf/flows.c index a7c233f7..ba934557 100644 --- a/bpf/flows.c +++ b/bpf/flows.c @@ -13,7 +13,6 @@ until an entry is available. 4) When hash collision is detected, we send the new entry to userpace via ringbuffer. */ -#define BPF_NO_PRESERVE_ACCESS_INDEX #include <vmlinux.h> #include <bpf_helpers.h> diff --git a/bpf/headers/bpf_core_read.h b/bpf/headers/bpf_core_read.h new file mode 100644 index 00000000..496e6a8e --- /dev/null +++ b/bpf/headers/bpf_core_read.h @@ -0,0 +1,484 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_CORE_READ_H__ +#define __BPF_CORE_READ_H__ + +/* + * enum bpf_field_info_kind is passed as a second argument into + * __builtin_preserve_field_info() built-in to get a specific aspect of + * a field, captured as a first argument. __builtin_preserve_field_info(field, + * info_kind) returns __u32 integer and produces BTF field relocation, which + * is understood and processed by libbpf during BPF object loading. See + * selftests/bpf for examples. + */ +enum bpf_field_info_kind { + BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ + BPF_FIELD_BYTE_SIZE = 1, + BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ + BPF_FIELD_SIGNED = 3, + BPF_FIELD_LSHIFT_U64 = 4, + BPF_FIELD_RSHIFT_U64 = 5, +}; + +/* second argument to __builtin_btf_type_id() built-in */ +enum bpf_type_id_kind { + BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */ + BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */ +}; + +/* second argument to __builtin_preserve_type_info() built-in */ +enum bpf_type_info_kind { + BPF_TYPE_EXISTS = 0, /* type existence in target kernel */ + BPF_TYPE_SIZE = 1, /* type size in target kernel */ + BPF_TYPE_MATCHES = 2, /* type match in target kernel */ +}; + +/* second argument to __builtin_preserve_enum_value() built-in */ +enum bpf_enum_value_kind { + BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */ + BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */ +}; + +#define __CORE_RELO(src, field, info) \ + __builtin_preserve_field_info((src)->field, BPF_FIELD_##info) + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ + bpf_probe_read_kernel( \ + (void *)dst, \ + __CORE_RELO(src, fld, BYTE_SIZE), \ + (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) +#else +/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so + * for big-endian we need to adjust destination pointer accordingly, based on + * field byte size + */ +#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \ + bpf_probe_read_kernel( \ + (void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \ + __CORE_RELO(src, fld, BYTE_SIZE), \ + (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET)) +#endif + +/* + * Extract bitfield, identified by s->field, and return its value as u64. + * All this is done in relocatable manner, so bitfield changes such as + * signedness, bit size, offset changes, this will be handled automatically. + * This version of macro is using bpf_probe_read_kernel() to read underlying + * integer storage. Macro functions as an expression and its return type is + * bpf_probe_read_kernel()'s return value: 0, on success, <0 on error. + */ +#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \ + unsigned long long val = 0; \ + \ + __CORE_BITFIELD_PROBE_READ(&val, s, field); \ + val <<= __CORE_RELO(s, field, LSHIFT_U64); \ + if (__CORE_RELO(s, field, SIGNED)) \ + val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ + else \ + val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ + val; \ +}) + +/* + * Extract bitfield, identified by s->field, and return its value as u64. + * This version of macro is using direct memory reads and should be used from + * BPF program types that support such functionality (e.g., typed raw + * tracepoints). + */ +#define BPF_CORE_READ_BITFIELD(s, field) ({ \ + const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \ + unsigned long long val; \ + \ + /* This is a so-called barrier_var() operation that makes specified \ + * variable "a black box" for optimizing compiler. \ + * It forces compiler to perform BYTE_OFFSET relocation on p and use \ + * its calculated value in the switch below, instead of applying \ + * the same relocation 4 times for each individual memory load. \ + */ \ + asm volatile("" : "=r"(p) : "0"(p)); \ + \ + switch (__CORE_RELO(s, field, BYTE_SIZE)) { \ + case 1: val = *(const unsigned char *)p; break; \ + case 2: val = *(const unsigned short *)p; break; \ + case 4: val = *(const unsigned int *)p; break; \ + case 8: val = *(const unsigned long long *)p; break; \ + } \ + val <<= __CORE_RELO(s, field, LSHIFT_U64); \ + if (__CORE_RELO(s, field, SIGNED)) \ + val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \ + else \ + val = val >> __CORE_RELO(s, field, RSHIFT_U64); \ + val; \ +}) + +#define ___bpf_field_ref1(field) (field) +#define ___bpf_field_ref2(type, field) (((typeof(type) *)0)->field) +#define ___bpf_field_ref(args...) \ + ___bpf_apply(___bpf_field_ref, ___bpf_narg(args))(args) + +/* + * Convenience macro to check that field actually exists in target kernel's. + * Returns: + * 1, if matching field is present in target kernel; + * 0, if no matching field found. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_exists(p->my_field); + * - field reference through type and field names: + * bpf_core_field_exists(struct my_type, my_field). + */ +#define bpf_core_field_exists(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_EXISTS) + +/* + * Convenience macro to get the byte size of a field. Works for integers, + * struct/unions, pointers, arrays, and enums. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_size(p->my_field); + * - field reference through type and field names: + * bpf_core_field_size(struct my_type, my_field). + */ +#define bpf_core_field_size(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_SIZE) + +/* + * Convenience macro to get field's byte offset. + * + * Supports two forms: + * - field reference through variable access: + * bpf_core_field_offset(p->my_field); + * - field reference through type and field names: + * bpf_core_field_offset(struct my_type, my_field). + */ +#define bpf_core_field_offset(field...) \ + __builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_OFFSET) + +/* + * Convenience macro to get BTF type ID of a specified type, using a local BTF + * information. Return 32-bit unsigned integer with type ID from program's own + * BTF. Always succeeds. + */ +#define bpf_core_type_id_local(type) \ + __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL) + +/* + * Convenience macro to get BTF type ID of a target kernel's type that matches + * specified local type. + * Returns: + * - valid 32-bit unsigned type ID in kernel BTF; + * - 0, if no matching type was found in a target kernel BTF. + */ +#define bpf_core_type_id_kernel(type) \ + __builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET) + +/* + * Convenience macro to check that provided named type + * (struct/union/enum/typedef) exists in a target kernel. + * Returns: + * 1, if such type is present in target kernel's BTF; + * 0, if no matching type is found. + */ +#define bpf_core_type_exists(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS) + +/* + * Convenience macro to check that provided named type + * (struct/union/enum/typedef) "matches" that in a target kernel. + * Returns: + * 1, if the type matches in the target kernel's BTF; + * 0, if the type does not match any in the target kernel + */ +#define bpf_core_type_matches(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES) + +/* + * Convenience macro to get the byte size of a provided named type + * (struct/union/enum/typedef) in a target kernel. + * Returns: + * >= 0 size (in bytes), if type is present in target kernel's BTF; + * 0, if no matching type is found. + */ +#define bpf_core_type_size(type) \ + __builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE) + +/* + * Convenience macro to check that provided enumerator value is defined in + * a target kernel. + * Returns: + * 1, if specified enum type and its enumerator value are present in target + * kernel's BTF; + * 0, if no matching enum and/or enum value within that enum is found. + */ +#define bpf_core_enum_value_exists(enum_type, enum_value) \ + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS) + +/* + * Convenience macro to get the integer value of an enumerator value in + * a target kernel. + * Returns: + * 64-bit value, if specified enum type and its enumerator value are + * present in target kernel's BTF; + * 0, if no matching enum and/or enum value within that enum is found. + */ +#define bpf_core_enum_value(enum_type, enum_value) \ + __builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE) + +/* + * bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures + * offset relocation for source address using __builtin_preserve_access_index() + * built-in, provided by Clang. + * + * __builtin_preserve_access_index() takes as an argument an expression of + * taking an address of a field within struct/union. It makes compiler emit + * a relocation, which records BTF type ID describing root struct/union and an + * accessor string which describes exact embedded field that was used to take + * an address. See detailed description of this relocation format and + * semantics in comments to struct bpf_field_reloc in libbpf_internal.h. + * + * This relocation allows libbpf to adjust BPF instruction to use correct + * actual field offset, based on target kernel BTF type that matches original + * (local) BTF, used to record relocation. + */ +#define bpf_core_read(dst, sz, src) \ + bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ +#define bpf_core_read_user(dst, sz, src) \ + bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src)) +/* + * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str() + * additionally emitting BPF CO-RE field relocation for specified source + * argument. + */ +#define bpf_core_read_str(dst, sz, src) \ + bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */ +#define bpf_core_read_user_str(dst, sz, src) \ + bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src)) + +#define ___concat(a, b) a ## b +#define ___apply(fn, n) ___concat(fn, n) +#define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N + +/* + * return number of provided arguments; used for switch-based variadic macro + * definitions (see ___last, ___arrow, etc below) + */ +#define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +/* + * return 0 if no arguments are passed, N - otherwise; used for + * recursively-defined macros to specify termination (0) case, and generic + * (N) case (e.g., ___read_ptrs, ___core_read) + */ +#define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0) + +#define ___last1(x) x +#define ___last2(a, x) x +#define ___last3(a, b, x) x +#define ___last4(a, b, c, x) x +#define ___last5(a, b, c, d, x) x +#define ___last6(a, b, c, d, e, x) x +#define ___last7(a, b, c, d, e, f, x) x +#define ___last8(a, b, c, d, e, f, g, x) x +#define ___last9(a, b, c, d, e, f, g, h, x) x +#define ___last10(a, b, c, d, e, f, g, h, i, x) x +#define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___nolast2(a, _) a +#define ___nolast3(a, b, _) a, b +#define ___nolast4(a, b, c, _) a, b, c +#define ___nolast5(a, b, c, d, _) a, b, c, d +#define ___nolast6(a, b, c, d, e, _) a, b, c, d, e +#define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f +#define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g +#define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h +#define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i +#define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___arrow1(a) a +#define ___arrow2(a, b) a->b +#define ___arrow3(a, b, c) a->b->c +#define ___arrow4(a, b, c, d) a->b->c->d +#define ___arrow5(a, b, c, d, e) a->b->c->d->e +#define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f +#define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g +#define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h +#define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i +#define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j +#define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) + +#define ___type(...) typeof(___arrow(__VA_ARGS__)) + +#define ___read(read_fn, dst, src_type, src, accessor) \ + read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) + +/* "recursively" read a sequence of inner pointers using local __t var */ +#define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a); +#define ___rd_last(fn, ...) \ + ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); +#define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__) +#define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__) +#define ___read_ptrs(fn, src, ...) \ + ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__) + +#define ___core_read0(fn, fn_ptr, dst, src, a) \ + ___read(fn, dst, ___type(src), src, a); +#define ___core_readN(fn, fn_ptr, dst, src, ...) \ + ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \ + ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \ + ___last(__VA_ARGS__)); +#define ___core_read(fn, fn_ptr, dst, src, a, ...) \ + ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \ + src, a, ##__VA_ARGS__) + +/* + * BPF_CORE_READ_INTO() is a more performance-conscious variant of + * BPF_CORE_READ(), in which final field is read into user-provided storage. + * See BPF_CORE_READ() below for more details on general usage. + */ +#define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read, bpf_core_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Variant of BPF_CORE_READ_INTO() for reading from user-space memory. + * + * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. + */ +#define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_user, bpf_core_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_INTO() */ +#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read, bpf_probe_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_USER_INTO(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_user, bpf_probe_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as + * BPF_CORE_READ() for intermediate pointers, but then executes (and returns + * corresponding error code) bpf_core_read_str() for final string read. + */ +#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_str, bpf_core_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory. + * + * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. + */ +#define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_core_read_user_str, bpf_core_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */ +#define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_str, bpf_probe_read, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \ + ___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \ + dst, (src), a, ##__VA_ARGS__) \ +}) + +/* + * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially + * when there are few pointer chasing steps. + * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like: + * int x = s->a.b.c->d.e->f->g; + * can be succinctly achieved using BPF_CORE_READ as: + * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g); + * + * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF + * CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically + * equivalent to: + * 1. const void *__t = s->a.b.c; + * 2. __t = __t->d.e; + * 3. __t = __t->f; + * 4. return __t->g; + * + * Equivalence is logical, because there is a heavy type casting/preservation + * involved, as well as all the reads are happening through + * bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to + * emit CO-RE relocations. + * + * N.B. Only up to 9 "field accessors" are supported, which should be more + * than enough for any practical purpose. + */ +#define BPF_CORE_READ(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* + * Variant of BPF_CORE_READ() for reading from user-space memory. + * + * NOTE: all the source types involved are still *kernel types* and need to + * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will + * fail. Custom user types are not relocatable with CO-RE. + * The typical situation in which BPF_CORE_READ_USER() might be used is to + * read kernel UAPI types from the user-space memory passed in as a syscall + * input argument. + */ +#define BPF_CORE_READ_USER(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* Non-CO-RE variant of BPF_CORE_READ() */ +#define BPF_PROBE_READ(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +/* + * Non-CO-RE variant of BPF_CORE_READ_USER(). + * + * As no CO-RE relocations are emitted, source types can be arbitrary and are + * not restricted to kernel types only. + */ +#define BPF_PROBE_READ_USER(src, a, ...) ({ \ + ___type((src), a, ##__VA_ARGS__) __r; \ + BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \ + __r; \ +}) + +#endif + diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index e2b77155..efd44c82 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -78,7 +78,7 @@ type ebpfFlowFetcher interface { io.Closer Register(iface ifaces.Interface) error - LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics + LookupAndDeleteMap() map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics ReadRingBuf() (ringbuf.Record, error) } @@ -352,8 +352,8 @@ func (f *Flows) buildAndStartPipeline(ctx context.Context) (*node.Terminal[[]*fl } alog.Debug("connecting flows' processing graph") - mapTracer := node.AsStart(f.mapTracer.TraceLoop(ctx)) - rbTracer := node.AsStart(f.rbTracer.TraceLoop(ctx)) + mapTracer := node.AsStart(f.mapTracer.TraceLoop(ctx, f.cfg.EnableGC)) + rbTracer := node.AsStart(f.rbTracer.TraceLoop(ctx, f.cfg.EnableGC)) accounter := node.AsMiddle(f.accounter.Account, node.ChannelBufferLen(f.cfg.BuffersLength)) diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index a8818ab9..1cf3a1ff 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -185,8 +185,8 @@ func testAgent(t *testing.T, cfg *Config) *test.ExporterFake { now := uint64(monotime.Now()) key1Metrics := ebpf.BpfFlowMetrics{Packets: 3, Bytes: 44, StartMonoTimeTs: now + 1000, EndMonoTimeTs: now + 1_000_000_000} - ebpfTracer.AppendLookupResults(map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics{ - key1: key1Metrics, + ebpfTracer.AppendLookupResults(map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics{ + key1: &key1Metrics, }) return export } diff --git a/pkg/agent/config.go b/pkg/agent/config.go index c9e9d913..6dfb49b2 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -136,4 +136,6 @@ type Config struct { KafkaSASLClientSecretPath string `env:"KAFKA_SASL_CLIENT_SECRET_PATH"` // ProfilePort sets the listening port for Go's Pprof tool. If it is not set, profile is disabled ProfilePort int `env:"PROFILE_PORT"` + // EnableGC enables golang garbage collection run at the end of every map eviction, default is true + EnableGC bool `env:"ENABLE_GARBAGE_COLLECTION" envDefault:"true"` } diff --git a/pkg/ebpf/bpf_bpfeb.o b/pkg/ebpf/bpf_bpfeb.o index d844353d9a0b367e85237787e1313b1b601b1c01..f3dd6b1ea09dea6751857a74838cac9ded0401a6 100644 GIT binary patch literal 27208 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 literal 22344 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 GO8@|}%0I9G diff --git a/pkg/ebpf/bpf_bpfel.o b/pkg/ebpf/bpf_bpfel.o index 75c4ad1b1b0fbd9c97adbcf8009bb3811e575f2c..b6095610414e142b585363b87c85ed31fa4fe8f5 100644 GIT binary patch literal 27280 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 delta 6581 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 Dq^&2c diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go index 4efa75bb..2253c1f4 100644 --- a/pkg/ebpf/tracer.go +++ b/pkg/ebpf/tracer.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/cilium/ebpf" + "github.com/cilium/ebpf/btf" "github.com/cilium/ebpf/ringbuf" "github.com/cilium/ebpf/rlimit" "github.com/netobserv/netobserv-ebpf-agent/pkg/ifaces" @@ -81,7 +82,12 @@ func NewFlowFetcher( } return nil, fmt.Errorf("loading and assigning BPF objects: %w", err) } - + /* + * since we load the program only when the we start we need to release + * memory used by cached kernel BTF see https://github.com/cilium/ebpf/issues/1063 + * for more details. + */ + btf.FlushKernelSpec() // read events from igress+egress ringbuffer flows, err := ringbuf.NewReader(objects.DirectFlows) if err != nil { @@ -124,7 +130,7 @@ func (m *FlowFetcher) Register(iface ifaces.Interface) error { if errors.Is(err, fs.ErrExist) { ilog.WithError(err).Warn("qdisc clsact already exists. Ignoring") } else { - return fmt.Errorf("failed to create clsact qdisc on %d (%s): %T %w", iface.Index, iface.Name, err, err) + return fmt.Errorf("failed to create clsact qdisc on %d (%s): %w", iface.Index, iface.Name, err) } } m.qdiscs[iface] = qdisc @@ -133,11 +139,7 @@ func (m *FlowFetcher) Register(iface ifaces.Interface) error { return err } - if err := m.registerIngress(iface, ipvlan); err != nil { - return err - } - - return nil + return m.registerIngress(iface, ipvlan) } func (m *FlowFetcher) registerEgress(iface ifaces.Interface, ipvlan netlink.Link) error { @@ -308,14 +310,14 @@ func (m *FlowFetcher) ReadRingBuf() (ringbuf.Record, error) { // TODO: detect whether BatchLookupAndDelete is supported (Kernel>=5.6) and use it selectively // Supported Lookup/Delete operations by kernel: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md // Race conditions here causes that some flows are lost in high-load scenarios -func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId]BpfFlowMetrics { +func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId]*BpfFlowMetrics { flowMap := m.objects.AggregatedFlows iterator := flowMap.Iterate() - var flow = make(map[BpfFlowId]BpfFlowMetrics, m.cacheMaxSize) - - id := BpfFlowId{} + var flow = make(map[BpfFlowId]*BpfFlowMetrics, m.cacheMaxSize) + var id BpfFlowId var metric BpfFlowMetrics + // Changing Iterate+Delete by LookupAndDelete would prevent some possible race conditions // TODO: detect whether LookupAndDelete is supported (Kernel>=4.20) and use it selectively for iterator.Next(&id, &metric) { @@ -323,10 +325,9 @@ func (m *FlowFetcher) LookupAndDeleteMap() map[BpfFlowId]BpfFlowMetrics { log.WithError(err).WithField("flowId", id). Warnf("couldn't delete flow entry") } - // We observed that eBFP PerCPU map might insert multiple times the same key in the map - // (probably due to race conditions) so we need to re-join metrics again at userspace - // TODO: instrument how many times the keys are is repeated in the same eviction - flow[id] = metric + metricPtr := new(BpfFlowMetrics) + *metricPtr = metric + flow[id] = metricPtr } return flow } diff --git a/pkg/exporter/ipfix.go b/pkg/exporter/ipfix.go index 67f18adc..e3066a75 100644 --- a/pkg/exporter/ipfix.go +++ b/pkg/exporter/ipfix.go @@ -327,7 +327,7 @@ func setEntities(record *flow.Record, elements *[]entities.InfoElementWithValue) setIERecordValue(record, &ieVal) } } -func (ipf *IPFIX) sendDataRecord(log *logrus.Entry, record *flow.Record, v6 bool) error { +func (ipf *IPFIX) sendDataRecord(_ *logrus.Entry, record *flow.Record, v6 bool) error { dataSet := entities.NewSet(false) var templateID uint16 if v6 { diff --git a/pkg/flow/tracer_map.go b/pkg/flow/tracer_map.go index 563c2850..e067e34e 100644 --- a/pkg/flow/tracer_map.go +++ b/pkg/flow/tracer_map.go @@ -2,6 +2,7 @@ package flow import ( "context" + "runtime" "sync" "time" @@ -25,7 +26,7 @@ type MapTracer struct { } type mapFetcher interface { - LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics + LookupAndDeleteMap() map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics } func NewMapTracer(fetcher mapFetcher, evictionTimeout time.Duration) *MapTracer { @@ -43,10 +44,10 @@ func (m *MapTracer) Flush() { m.evictionCond.Broadcast() } -func (m *MapTracer) TraceLoop(ctx context.Context) node.StartFunc[[]*Record] { +func (m *MapTracer) TraceLoop(ctx context.Context, enableGC bool) node.StartFunc[[]*Record] { return func(out chan<- []*Record) { evictionTicker := time.NewTicker(m.evictionTimeout) - go m.evictionSynchronization(ctx, out) + go m.evictionSynchronization(ctx, enableGC, out) for { select { case <-ctx.Done(): @@ -64,7 +65,7 @@ func (m *MapTracer) TraceLoop(ctx context.Context) node.StartFunc[[]*Record] { // evictionSynchronization loop just waits for the evictionCond to happen // and triggers the actual eviction. It makes sure that only one eviction // is being triggered at the same time -func (m *MapTracer) evictionSynchronization(ctx context.Context, out chan<- []*Record) { +func (m *MapTracer) evictionSynchronization(ctx context.Context, enableGC bool, out chan<- []*Record) { // flow eviction loop. It just keeps waiting for eviction until someone triggers the // evictionCond.Broadcast signal for { @@ -77,14 +78,14 @@ func (m *MapTracer) evictionSynchronization(ctx context.Context, out chan<- []*R return default: mtlog.Debug("evictionSynchronization signal received") - m.evictFlows(ctx, out) + m.evictFlows(ctx, enableGC, out) } m.evictionCond.L.Unlock() } } -func (m *MapTracer) evictFlows(ctx context.Context, forwardFlows chan<- []*Record) { +func (m *MapTracer) evictFlows(ctx context.Context, enableGC bool, forwardFlows chan<- []*Record) { // it's important that this monotonic timer reports same or approximate values as kernel-side bpf_ktime_get_ns() monotonicTimeNow := monotime.Now() currentTime := time.Now() @@ -103,7 +104,7 @@ func (m *MapTracer) evictFlows(ctx context.Context, forwardFlows chan<- []*Recor } forwardingFlows = append(forwardingFlows, NewRecord( flowKey, - aggregatedMetrics, + *aggregatedMetrics, currentTime, uint64(monotonicTimeNow), )) @@ -115,5 +116,9 @@ func (m *MapTracer) evictFlows(ctx context.Context, forwardFlows chan<- []*Recor default: forwardFlows <- forwardingFlows } + + if enableGC { + runtime.GC() + } mtlog.Debugf("%d flows evicted", len(forwardingFlows)) } diff --git a/pkg/flow/tracer_ringbuf.go b/pkg/flow/tracer_ringbuf.go index bd6e3791..eadfcea8 100644 --- a/pkg/flow/tracer_ringbuf.go +++ b/pkg/flow/tracer_ringbuf.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "runtime" "sync/atomic" "syscall" "time" @@ -51,7 +52,7 @@ func NewRingBufTracer( } } -func (m *RingBufTracer) TraceLoop(ctx context.Context) node.StartFunc[*RawRecord] { +func (m *RingBufTracer) TraceLoop(ctx context.Context, enableGC bool) node.StartFunc[*RawRecord] { return func(out chan<- *RawRecord) { debugging := logrus.IsLevelEnabled(logrus.DebugLevel) for { @@ -60,7 +61,7 @@ func (m *RingBufTracer) TraceLoop(ctx context.Context) node.StartFunc[*RawRecord rtlog.Debug("exiting trace loop due to context cancellation") return default: - if err := m.listenAndForwardRingBuffer(debugging, out); err != nil { + if err := m.listenAndForwardRingBuffer(debugging, enableGC, out); err != nil { if errors.Is(err, ringbuf.ErrClosed) { rtlog.Debug("Received signal, exiting..") return @@ -73,7 +74,7 @@ func (m *RingBufTracer) TraceLoop(ctx context.Context) node.StartFunc[*RawRecord } } -func (m *RingBufTracer) listenAndForwardRingBuffer(debugging bool, forwardCh chan<- *RawRecord) error { +func (m *RingBufTracer) listenAndForwardRingBuffer(debugging, enableGC bool, forwardCh chan<- *RawRecord) error { event, err := m.ringBuffer.ReadRingBuf() if err != nil { return fmt.Errorf("reading from ring buffer: %w", err) @@ -95,7 +96,9 @@ func (m *RingBufTracer) listenAndForwardRingBuffer(debugging bool, forwardCh cha // Will need to send it to accounter anyway to account regardless of complete/ongoing flow forwardCh <- readFlow - + if enableGC { + runtime.GC() + } return nil } diff --git a/pkg/test/tracer_fake.go b/pkg/test/tracer_fake.go index 495acfca..960de0c6 100644 --- a/pkg/test/tracer_fake.go +++ b/pkg/test/tracer_fake.go @@ -13,14 +13,14 @@ import ( // TracerFake fakes the kernel-side eBPF map structures for testing type TracerFake struct { interfaces map[ifaces.Interface]struct{} - mapLookups chan map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics + mapLookups chan map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics ringBuf chan ringbuf.Record } func NewTracerFake() *TracerFake { return &TracerFake{ interfaces: map[ifaces.Interface]struct{}{}, - mapLookups: make(chan map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics, 100), + mapLookups: make(chan map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics, 100), ringBuf: make(chan ringbuf.Record, 100), } } @@ -33,12 +33,12 @@ func (m *TracerFake) Register(iface ifaces.Interface) error { return nil } -func (m *TracerFake) LookupAndDeleteMap() map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics { +func (m *TracerFake) LookupAndDeleteMap() map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics { select { case r := <-m.mapLookups: return r default: - return map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics{} + return map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics{} } } @@ -46,7 +46,7 @@ func (m *TracerFake) ReadRingBuf() (ringbuf.Record, error) { return <-m.ringBuf, nil } -func (m *TracerFake) AppendLookupResults(results map[ebpf.BpfFlowId]ebpf.BpfFlowMetrics) { +func (m *TracerFake) AppendLookupResults(results map[ebpf.BpfFlowId]*ebpf.BpfFlowMetrics) { m.mapLookups <- results } diff --git a/scripts/update-bpf-headers.sh b/scripts/update-bpf-headers.sh index 38f97d87..ce0301b2 100755 --- a/scripts/update-bpf-headers.sh +++ b/scripts/update-bpf-headers.sh @@ -11,6 +11,7 @@ headers=( "$prefix"/src/bpf_helper_defs.h "$prefix"/src/bpf_helpers.h "$prefix"/src/bpf_tracing.h + "$prefix"/src/bpf_core_read.h ) # Fetch libbpf release and extract the desired headers -- GitLab