Skip to content

Commit

Permalink
map logic refactoring and standardization
Browse files Browse the repository at this point in the history
Moved C definitions to 'libbpfgo.c', leaving 'libbpfgo.h' only with
declarations to avoid multiple declaration errors when importing
'libbpfgo.h' from different Go files.

Introduce 'libbpfgo_bpf_map_batch_opts_new' and
'libbpfgo_bpf_map_batch_opts_free' struct helpers to manage the C struct
lifecycle on the C side. These are replacements for 'BPFMapBatchOpts'
and 'bpfMapBatchOptsToC()'. This avoids problems with structs which
may contain bitfields. See #244.

Prefixed all C helpers used in Go with 'libbpfgo'.

Restructured libbpf BPF map logic into separate files:
- 'map-common.go' contains generic BPF map types, constants, and helpers
   like 'GetMapInfoByFD()' and 'GetMapFDByID()'.
- 'map-low.go' includes 'BPFMapLow' and low-level helpers like the newly
   introduced 'GetMapByID()'.
- 'map-iterator.go' contains related types and logic.

Introduced 'misc.go' as a place for miscellaneous generic helpers and
'btf.go' with the new 'GetBTFFDByID()' function, as a place for BTF
related helpers.

In the 'BPFMap' struct:
- Removed fields like 'fd' and 'name' since they must be retrieved
  directly from 'libbpf'.
- Added 'bpfMapLow' instance for access to low-level methods.
- Deprecated 'BPFMap.GetMaxEntries' in favor of using 'MaxEntries'.
- Exposed 'InitialValue' and 'SetInitialValue' as public wrappers.

Bug Fixes:
- Fixed cases where 'BPFMap.ValueSize()', possibly 0, was being passed
  directly to 'make()' - detected on the map of maps effort #343.
- Fixed 'get_internal_map_init_value' which didn't check against NULL.

Further Refactoring and Standardization on the map related logic and on
lines touched by other changes:
- Removed check for 'BPFMap.Name()' against nil, as 'C.GoString(C.NULL)'
  returns "".
- Changed error returns to 'retC' in functions that return error values
  to avoid confusion with possible use of errno.
- Changed returns to 'valueC' in functions that return values or
  pointers to values.
- Suffixed variable names that are C types with 'C'.
- Applied the use of 'defer' for better resource management.
  • Loading branch information
geyslan committed Aug 9, 2023
1 parent d45e8f6 commit 7f40338
Show file tree
Hide file tree
Showing 10 changed files with 1,507 additions and 819 deletions.
26 changes: 26 additions & 0 deletions btf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package libbpfgo

/*
#cgo LDFLAGS: -lelf -lz
#include "libbpfgo.h"
*/
import "C"

import (
"fmt"
"syscall"
)

//
// BTF (low-level API)
//

// GetBTFFDByID returns a file descriptor for the BTF with the given ID.
func GetBTFFDByID(id uint32) (int, error) {
fdC := C.bpf_btf_get_fd_by_id(C.uint(id))
if fdC < 0 {
return int(fdC), fmt.Errorf("could not find BTF id %d: %w", id, syscall.Errno(-fdC))
}

return int(fdC), nil
}
238 changes: 238 additions & 0 deletions libbpfgo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#include "libbpfgo.h"

extern void loggerCallback(enum libbpf_print_level level, char *output);
extern void perfCallback(void *ctx, int cpu, void *data, __u32 size);
extern void perfLostCallback(void *ctx, int cpu, __u64 cnt);
extern int ringbufferCallback(void *ctx, void *data, size_t size);

int libbpf_print_fn(enum libbpf_print_level level, // libbpf print level
const char *format, // format used for the msg
va_list args)
{ // args used by format

int ret;
size_t len;
char *out;
va_list check;

va_copy(check, args);
ret = vsnprintf(NULL, 0, format, check); // get output length
va_end(check);

if (ret < 0)
return ret;

len = ret + 1; // add 1 for NUL
out = malloc(len);
if (!out)
return -ENOMEM;

va_copy(check, args);
ret = vsnprintf(out, len, format, check);
va_end(check);

if (ret > 0)
loggerCallback(level, out);

free(out);

return ret;
}

void libbpfgo_libbpf_set_print_fn()
{
libbpf_set_print(libbpf_print_fn);
}

struct ring_buffer *libbpfgo_init_ring_buf(int map_fd, uintptr_t ctx)
{
struct ring_buffer *rb = NULL;

rb = ring_buffer__new(map_fd, ringbufferCallback, (void *) ctx, NULL);
if (!rb) {
int saved_errno = errno;
fprintf(stderr, "Failed to initialize ring buffer: %s\n", strerror(errno));
errno = saved_errno;

return NULL;
}

return rb;
}

struct perf_buffer *libbpfgo_init_perf_buf(int map_fd, int page_cnt, uintptr_t ctx)
{
struct perf_buffer_opts pb_opts = {};
struct perf_buffer *pb = NULL;

pb_opts.sz = sizeof(struct perf_buffer_opts);

pb = perf_buffer__new(map_fd, page_cnt, perfCallback, perfLostCallback, (void *) ctx, &pb_opts);
if (!pb) {
int saved_errno = errno;
fprintf(stderr, "Failed to initialize perf buffer: %s\n", strerror(errno));
errno = saved_errno;

return NULL;
}

return pb;
}

void libbpfgo_bpf_map__initial_value(struct bpf_map *map, void *value)
{
size_t psize;
const void *data;

data = bpf_map__initial_value(map, &psize);
if (!data)
return;

memcpy(value, data, psize);
}

int libbpfgo_bpf_prog_attach_cgroup_legacy(int prog_fd, // eBPF program file descriptor
int target_fd, // cgroup directory file descriptor
int type) // BPF_CGROUP_INET_{INGRESS,EGRESS}, ...
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.target_fd = target_fd;
attr.attach_bpf_fd = prog_fd;
attr.attach_type = type;
attr.attach_flags = BPF_F_ALLOW_MULTI; // or BPF_F_ALLOW_OVERRIDE

return syscall(__NR_bpf, BPF_PROG_ATTACH, &attr, sizeof(attr));
}

int libbpfgo_bpf_prog_detach_cgroup_legacy(int prog_fd, // eBPF program file descriptor
int target_fd, // cgroup directory file descriptor
int type) // BPF_CGROUP_INET_{INGRESS,EGRESS}, ...
{
union bpf_attr attr;
memset(&attr, 0, sizeof(attr));
attr.target_fd = target_fd;
attr.attach_bpf_fd = prog_fd;
attr.attach_type = type;

return syscall(__NR_bpf, BPF_PROG_DETACH, &attr, sizeof(attr));
}

struct bpf_iter_attach_opts *libbpfgo_bpf_iter_attach_opts_new(__u32 map_fd,
enum bpf_cgroup_iter_order order,
__u32 cgroup_fd,
__u64 cgroup_id,
__u32 tid,
__u32 pid,
__u32 pid_fd)
{
union bpf_iter_link_info *linfo;
linfo = calloc(1, sizeof(*linfo));
if (!linfo)
return NULL;

linfo->map.map_fd = map_fd;
linfo->cgroup.order = order;
linfo->cgroup.cgroup_fd = cgroup_fd;
linfo->cgroup.cgroup_id = cgroup_id;
linfo->task.tid = tid;
linfo->task.pid = pid;
linfo->task.pid_fd = pid_fd;

struct bpf_iter_attach_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts) {
free(linfo);
return NULL;
}

opts->sz = sizeof(*opts);
opts->link_info_len = sizeof(*linfo);
opts->link_info = linfo;

return opts;
}

void libbpfgo_bpf_iter_attach_opts_free(struct bpf_iter_attach_opts *opts)
{
if (!opts)
return;

free(opts->link_info);
free(opts);
}

struct bpf_object_open_opts *libbpfgo_bpf_object_open_opts_new(const char *btf_file_path,
const char *kconfig_path,
const char *bpf_obj_name)
{
struct bpf_object_open_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->btf_custom_path = btf_file_path;
opts->kconfig = kconfig_path;
opts->object_name = bpf_obj_name;

return opts;
}

void libbpfgo_bpf_object_open_opts_free(struct bpf_object_open_opts *opts)
{
free(opts);
}

struct bpf_map_create_opts *libbpfgo_bpf_map_create_opts_new(__u32 btf_fd,
__u32 btf_key_type_id,
__u32 btf_value_type_id,
__u32 btf_vmlinux_value_type_id,
__u32 inner_map_fd,
__u32 map_flags,
__u64 map_extra,
__u32 numa_node,
__u32 map_ifindex)
{
struct bpf_map_create_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->btf_fd = btf_fd;
opts->btf_key_type_id = btf_key_type_id;
opts->btf_value_type_id = btf_value_type_id;
opts->btf_vmlinux_value_type_id = btf_vmlinux_value_type_id;
opts->inner_map_fd = inner_map_fd;
opts->map_flags = map_flags;
opts->map_extra = map_extra;
opts->numa_node = numa_node;
opts->map_ifindex = map_ifindex;

return opts;
}

void libbpfgo_bpf_map_create_opts_free(struct bpf_map_create_opts *opts)
{
free(opts);
}

struct bpf_map_batch_opts *libbpfgo_bpf_map_batch_opts_new(__u64 elem_flags, __u64 flags)
{
struct bpf_map_batch_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->elem_flags = elem_flags;
opts->flags = flags;

return opts;
}

void libbpfgo_bpf_map_batch_opts_free(struct bpf_map_batch_opts *opts)
{
free(opts);
}
Loading

0 comments on commit 7f40338

Please sign in to comment.