elastic-ebpf/GPL/Events/Varlen.h (40 lines of code) (raw):
// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
/*
* Copyright (C) 2022 Elasticsearch BV
*
* This software is dual-licensed under the BSD 2-Clause and GPL v2 licenses.
* You may choose either one of them if you use this software.
*/
// Utilities for dealing with variable-length fields
#ifndef EBPF_EVENTS_VARLEN_H
#define EBPF_EVENTS_VARLEN_H
#include "EbpfEventProto.h"
// We can't use the ringbuf reserve/commit API if we want to output an event
// with variable length fields as we won't know the event size in advance, so
// we create events on the event_buffer_map if this is the case and output them
// with ebpf_ringbuf_write.
//
// If the event has no variable length parameters (i.e. is always a fixed
// size). bpf_ringbuf_reserve/bpf_ringbuf_submit should be used instead to
// avoid the extra memory copy for better performance.
// 256 KiB per cpu core, of which 128 KiB is useable as we have to bound each
// new variable-length field to start at no more than half the size of the
// buffer to make the verifier happy.
//
// 128 KiB is currently more than large enough to handle the largest
// theoretical event, but should be bumped in the future if that changes or
// else the verifier will start to complain.
#define EVENT_BUFFER_SIZE (1 << 18)
#define EVENT_BUFFER_SIZE_HALF (EVENT_BUFFER_SIZE >> 1)
#define EVENT_BUFFER_SIZE_HALF_MASK (EVENT_BUFFER_SIZE_HALF - 1)
// Convenience macro to determine the current size of an event with its
// variable length fields
//
// We logical and with (EVENT_BUFFER_SIZE - 1). This puts both an upper and
// lower bound on the value so that we have 0 <= value < EVENT_BUFFER_SIZE, and
// the verifier is happy.
#define EVENT_SIZE(x) ((sizeof(*x) + x->vl_fields.size) & (EVENT_BUFFER_SIZE - 1))
// Using a BPF_MAP_TYPE_PERCPU_ARRAY here would be simpler but unfortunately we
// can't use one. The allocation of map values for a BPF_MAP_TYPE_PERCPU_ARRAY
// is done under the hood by Linux's percpu allocator, which has a maximum
// allocation size of 32 KiB (See PCPU_MIN_UNIT_SIZE in include/linux/percpu.h
// as of Linux 6.0). Trying to create a BPF_MAP_TYPE_PERCPU_ARRAY with a value
// size larger than 32 KiB will result in -ENOMEM.
//
// This is too small for us, so instead we implement a percpu array ourselves
// using a BPF_MAP_TYPE_ARRAY. We resize it in userspace to $(nproc) elements
// and access it through the get_event_buffer helper, which returns the value
// corresponding to the current processor.
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, EVENT_BUFFER_SIZE);
__uint(max_entries, 0); // Will be resized by userspace to $(nproc)
} event_buffer_map SEC(".maps");
static void *get_event_buffer()
{
int key = bpf_get_smp_processor_id();
return bpf_map_lookup_elem(&event_buffer_map, &key);
}
struct ebpf_varlen_field *ebpf_vl_field__add(struct ebpf_varlen_fields_start *fields,
enum ebpf_varlen_field_type type)
{
struct ebpf_varlen_field *new_field =
(struct ebpf_varlen_field *)(&fields->data[fields->size & EVENT_BUFFER_SIZE_HALF_MASK]);
new_field->type = type;
fields->nfields++;
return new_field;
}
void ebpf_vl_fields__init(struct ebpf_varlen_fields_start *fields)
{
fields->nfields = 0;
fields->size = 0;
}
void ebpf_vl_field__set_size(struct ebpf_varlen_fields_start *vl_fields,
struct ebpf_varlen_field *field,
size_t size)
{
vl_fields->size += size + sizeof(struct ebpf_varlen_field);
field->size = size;
}
#endif // EBPF_EVENTS_VARLEN_H