Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(modern_bpf): [EXPERIMENTAL] support variable number of ring buffers and online CPUs #820

Merged
merged 6 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
11 changes: 5 additions & 6 deletions driver/modern_bpf/maps/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ struct

/**
* @brief For every CPU on the system we have a counter
* map where we store the number of events correcty pushed
* map where we store the number of events correctly pushed
* and the number of events dropped.
*/
struct
Expand All @@ -154,18 +154,17 @@ struct
/*=============================== RINGBUF MAP ===============================*/

/**
* @brief We will have a ringbuf map for every CPU on the system.
* The dimension of the single ringbuf and the number of
* ringbuf maps are set in userspace.
* @brief We use this map to let the verifier understand the content of our array of maps (`ringbuf_maps`)
*/
struct ringbuf_map
{
__uint(type, BPF_MAP_TYPE_RINGBUF);
};

/**
* @brief This array of maps will contain a ringbuf map for every CPU
* on the system.
* @brief This array of maps will contain a variable number of ring buffers
* according to the user-provided configuration. It could also contain only
* one buffer shared between all CPUs.
*/
struct
{
Expand Down
29 changes: 18 additions & 11 deletions userspace/libpman/include/libpman.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern "C"
#endif

/* `libpman` return values convention:
* In case of success `0` is return otherwise `errno`. If `errno` is not
* In case of success `0` is returned otherwise `errno`. If `errno` is not
* available `-1` is returned.
*
* Please Note:
Expand All @@ -47,17 +47,24 @@ extern "C"
*
* @param verbosity use `true` if you want to activate libbpf verbosity.
* @param buf_bytes_dim dimension of a single per-CPU buffer in bytes.
* @param cpus_for_each_buffer number of CPUs to which we want to associate a ring buffer.
* @param allocate_online_only if true, allocate ring buffers taking only into account online CPUs.
* @return `0` on success, `-1` in case of error.
*/
int pman_init_state(bool verbosity, unsigned long buf_bytes_dim);
int pman_init_state(bool verbosity, unsigned long buf_bytes_dim, uint16_t cpus_for_each_buffer, bool allocate_online_only);

/**
* @brief Return the number of available CPUs on the system, not the
* online CPUs!
* @brief Clear the `libpman` global state before it is used.
* This API could be useful if we open the modern bpf engine multiple times.
*/
void pman_clear_state(void);

/**
* @brief Return the number of allocated ring buffers.
*
* @return number of available CPUs on success, `-1` in case of error.
* @return number of allocated ring buffers.
*/
int pman_get_cpus_number(void);
int pman_get_required_buffers(void);

/////////////////////////////
// PROBE LIFECYCLE
Expand Down Expand Up @@ -225,10 +232,10 @@ extern "C"
*
* @param event_ptr in case of success return a pointer
* to the event, otherwise return NULL.
* @param cpu_id in case of success returns the id of the CPU
* on which we have found the event, otherwise return `-1`.
* @param buffer_id in case of success returns the id of the ring buffer
* from which we retrieved the event, otherwise return `-1`.
*/
void pman_consume_first_from_buffers(void** event_ptr, int16_t *cpu_id);
void pman_consume_first_event(void** event_ptr, int16_t* buffer_id);

/////////////////////////////
// CAPTURE (EXCHANGE VALUES WITH BPF SIDE)
Expand Down Expand Up @@ -413,15 +420,15 @@ extern "C"
* @brief Return `true` if all ring buffers are full. To state
* that a ring buffer is full we check that the free space is less
* than the `threshold`
*
*
* @param threshold used to check if a buffer is full
* @return `true` if all buffers are full, otherwise `false`
*/
bool pman_are_all_ringbuffers_full(unsigned long threshold);

/**
* @brief Get the producer pos for the required ring
*
*
* @param ring_num ring for which we want to obtain the producer pos
* @return producer pos as an unsigned long
*/
Expand Down
19 changes: 15 additions & 4 deletions userspace/libpman/src/capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ int pman_enable_capture(bool *tp_set)

int pman_disable_capture()
{
return pman_detach_all_programs();
/* If we fail at initialization time the BPF skeleton is not initialized */
if(g_state.skel)
{
return pman_detach_all_programs();
}
return 0;
}

#ifdef TEST_HELPERS
Expand All @@ -61,7 +66,7 @@ int pman_print_stats()
return errno;
}

for(int index = 0; index < g_state.n_cpus; index++)
for(int index = 0; index < g_state.n_possible_cpus; index++)
{
if(bpf_map_lookup_elem(counter_maps_fd, &index, &cnt_map) < 0)
{
Expand Down Expand Up @@ -109,7 +114,10 @@ int pman_get_scap_stats(void *scap_stats_struct)
* - stats->n_preemptions
*/

for(int index = 0; index < g_state.n_cpus; index++)
/* We always take statistics from all the CPUs, even if some of them are not online.
* If the CPU is not online the counter map will be empty.
*/
for(int index = 0; index < g_state.n_possible_cpus; index++)
{
if(bpf_map_lookup_elem(counter_maps_fd, &index, &cnt_map) < 0)
{
Expand Down Expand Up @@ -141,7 +149,10 @@ int pman_get_n_tracepoint_hit(long *n_events_per_cpu)
return errno;
}

for(int index = 0; index < g_state.n_cpus; index++)
/* We always take statistics from all the CPUs, even if some of them are not online.
* If the CPU is not online the counter map will be empty.
*/
for(int index = 0; index < g_state.n_possible_cpus; index++)
{
if(bpf_map_lookup_elem(counter_maps_fd, &index, &cnt_map) < 0)
{
Expand Down
81 changes: 75 additions & 6 deletions userspace/libpman/src/configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,27 @@ static void setup_libbpf_logging(bool verbosity)
}
}

int pman_init_state(bool verbosity, unsigned long buf_bytes_dim)
void pman_clear_state()
{
g_state.skel = NULL;
g_state.rb_manager = NULL;
g_state.n_possible_cpus = 0;
g_state.n_interesting_cpus = 0;
g_state.allocate_online_only = false;
g_state.n_required_buffers = 0;
g_state.cpus_for_each_buffer = 0;
g_state.ringbuf_pos = 0;
g_state.cons_pos = NULL;
g_state.prod_pos = NULL;
g_state.inner_ringbuf_map_fd = 0;
g_state.buffer_bytes_dim = 0;
g_state.last_ring_read = -1;
g_state.last_event_size = 0;
}

int pman_init_state(bool verbosity, unsigned long buf_bytes_dim, uint16_t cpus_for_each_buffer, bool allocate_online_only)
{
char error_message[MAX_ERROR_MESSAGE_LEN];

/* `LIBBPF_STRICT_ALL` turns on all supported strict features
* of libbpf to simulate libbpf v1.0 behavior.
Expand All @@ -57,14 +76,64 @@ int pman_init_state(bool verbosity, unsigned long buf_bytes_dim)
setup_libbpf_logging(verbosity);

/* Set the available number of CPUs inside the internal state. */
g_state.n_cpus = libbpf_num_possible_cpus();
if(g_state.n_cpus <= 0)
g_state.n_possible_cpus = libbpf_num_possible_cpus();
if(g_state.n_possible_cpus <= 0)
{
pman_print_error("no available cpus");
return -1;
}

/* Set the dimension of a single per-CPU ring buffer. */
g_state.allocate_online_only = allocate_online_only;

if(g_state.allocate_online_only)
{
ssize_t online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
if(online_cpus != -1)
{
/* We will allocate buffers only for online CPUs */
g_state.n_interesting_cpus = online_cpus;
}
else
{
/* Fallback to all available CPU even if the `allocate_online_only` flag is set to `true` */
g_state.n_interesting_cpus = g_state.n_possible_cpus;
}
}
else
{
/* We will allocate buffers only for all available CPUs */
g_state.n_interesting_cpus = g_state.n_possible_cpus;
}

/* We are requiring a buffer every `cpus_for_each_buffer` CPUs,
* but `cpus_for_each_buffer` is greater than our possible CPU number!
*/
if(cpus_for_each_buffer > g_state.n_interesting_cpus)
{
snprintf(error_message, MAX_ERROR_MESSAGE_LEN, "we are requiring a buffer every '%d' CPUs, but '%d' is greater than our interesting CPU number (%d)!", cpus_for_each_buffer, cpus_for_each_buffer, g_state.n_interesting_cpus);
pman_print_error((const char*)error_message);
return -1;
}

/* `0` is a special value that means a single ring buffer shared between all the CPUs */
if(cpus_for_each_buffer == 0)
{
/* We want a single ring buffer so 1 ring buffer for all the interesting CPUs we have */
g_state.cpus_for_each_buffer = g_state.n_interesting_cpus;
}
else
{
g_state.cpus_for_each_buffer = cpus_for_each_buffer;
}

/* Set the number of ring buffers we need */
g_state.n_required_buffers = g_state.n_interesting_cpus / g_state.cpus_for_each_buffer;
/* If we have some remaining CPUs it means that we need another buffer */
if((g_state.n_interesting_cpus % g_state.cpus_for_each_buffer) != 0)
{
g_state.n_required_buffers++;
}
/* Set the dimension of a single ring buffer */
g_state.buffer_bytes_dim = buf_bytes_dim;

/* These will be used during the ring buffer consumption phase. */
Expand All @@ -73,7 +142,7 @@ int pman_init_state(bool verbosity, unsigned long buf_bytes_dim)
return 0;
}

int pman_get_cpus_number()
int pman_get_required_buffers()
{
return g_state.n_cpus;
return g_state.n_required_buffers;
}
9 changes: 5 additions & 4 deletions userspace/libpman/src/lifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,23 @@ int pman_load_probe()

void pman_close_probe()
{
if(!g_state.cons_pos)
if(g_state.cons_pos)
{
free(g_state.cons_pos);
}

if(!g_state.prod_pos)
if(g_state.prod_pos)
{
free(g_state.prod_pos);
}

if(!g_state.skel)
if(g_state.skel)
{
bpf_probe__detach(g_state.skel);
bpf_probe__destroy(g_state.skel);
}

if(!g_state.rb_manager)
if(g_state.rb_manager)
{
ring_buffer__free(g_state.rb_manager);
}
Expand Down
6 changes: 4 additions & 2 deletions userspace/libpman/src/maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ int pman_fill_extra_event_prog_tail_table()

static int size_auxiliary_maps()
{
if(bpf_map__set_max_entries(g_state.skel->maps.auxiliary_maps, g_state.n_cpus))
/* We always allocate auxiliary maps from all the CPUs, even if some of them are not online. */
if(bpf_map__set_max_entries(g_state.skel->maps.auxiliary_maps, g_state.n_possible_cpus))
{
pman_print_error("unable to set max entries for 'auxiliary_maps'");
return errno;
Expand All @@ -254,7 +255,8 @@ static int size_auxiliary_maps()

static int size_counter_maps()
{
if(bpf_map__set_max_entries(g_state.skel->maps.counter_maps, g_state.n_cpus))
/* We always allocate counter maps from all the CPUs, even if some of them are not online. */
if(bpf_map__set_max_entries(g_state.skel->maps.counter_maps, g_state.n_possible_cpus))
{
pman_print_error(" unable to set max entries for 'counter_maps'");
return errno;
Expand Down
Loading