Skip to content

Commit

Permalink
chore(map): logic refactoring and standardization
Browse files Browse the repository at this point in the history
Introduced struct helpers to manage the C struct lifecycle on the C side,
which avoids problems with structs which may contain bitfields. See #244.

  These are replacements for 'BPFMapBatchOpts' and 'bpfMapBatchOptsToC()'.
  - libbpfgo_bpf_map_batch_opts_new
  - libbpfgo_bpf_map_batch_opts_free

  These are replacements for 'bpfMapCreateOptsToC()'
  - libbpfgo_bpf_map_create_opts_new
  - libbpfgo_bpf_map_create_opts_free

Restructured libbpf BPF map logic into separate files:
- 'map-common.go' contains generic BPF map types, constants, and helpers
  like 'GetMapInfoByFD()'.
- 'map-low.go' includes 'BPFMapLow' and respective logic and helpers
  like 'CreateMap()'.
- 'map-iterator.go' contains related types and logic.

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.

Introduced 'misc.go' as a place for miscellaneous generic helpers.

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

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 c5dfa35 commit ba052a5
Show file tree
Hide file tree
Showing 9 changed files with 1,094 additions and 507 deletions.
53 changes: 53 additions & 0 deletions libbpfgo.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,56 @@ 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);
}
69 changes: 55 additions & 14 deletions libbpfgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ func (m *Module) InitGlobalVariable(name string, value interface{}) error {
}

// get current value
currMapValue := bpfMap.getInitialValue()
currMapValue, err := bpfMap.InitialValue()
if err != nil {
return err
}

// generate new value
newMapValue := make([]byte, bpfMap.ValueSize())
Expand All @@ -386,24 +389,44 @@ func (m *Module) InitGlobalVariable(name string, value interface{}) error {
copy(newMapValue[start:end], varValue)

// save new value
err = bpfMap.setInitialValue(unsafe.Pointer(&newMapValue[0]))
err = bpfMap.SetInitialValue(unsafe.Pointer(&newMapValue[0]))
return err
}

func (m *Module) GetMap(mapName string) (*BPFMap, error) {
cs := C.CString(mapName)
bpfMap, errno := C.bpf_object__find_map_by_name(m.obj, cs)
bpfMapC, errno := C.bpf_object__find_map_by_name(m.obj, cs)
C.free(unsafe.Pointer(cs))
if bpfMap == nil {
if bpfMapC == nil {
return nil, fmt.Errorf("failed to find BPF map %s: %w", mapName, errno)
}

return &BPFMap{
bpfMap: bpfMap,
name: mapName,
fd: C.bpf_map__fd(bpfMap),
bpfMap := &BPFMap{
bpfMap: bpfMapC,
module: m,
}, nil
}

if !m.loaded {
bpfMap.bpfMapLow = &BPFMapLow{
fd: -1,
info: &BPFMapInfo{},
}

return bpfMap, nil
}

fd := bpfMap.FileDescriptor()
info, err := GetMapInfoByFD(fd)
if err != nil {
return nil, err
}

bpfMap.bpfMapLow = &BPFMapLow{
fd: fd,
info: info,
}

return bpfMap, nil
}

// BPFObjectProgramIterator iterates over maps in a BPF object
Expand Down Expand Up @@ -431,15 +454,33 @@ func (it *BPFObjectIterator) NextMap() *BPFMap {
if m == nil {
return nil
}
cName := C.bpf_map__name(m)

bpfMap := &BPFMap{
name: C.GoString(cName),
bpfMap: m,
module: it.m,
}

it.prevMap = bpfMap

if !bpfMap.module.loaded {
bpfMap.bpfMapLow = &BPFMapLow{
fd: -1,
info: &BPFMapInfo{},
}

return bpfMap
}

fd := bpfMap.FileDescriptor()
info, err := GetMapInfoByFD(fd)
if err != nil {
return nil
}

bpfMap.bpfMapLow = &BPFMapLow{
fd: fd,
info: info,
}

return bpfMap
}

Expand Down Expand Up @@ -1150,7 +1191,7 @@ func (m *Module) InitRingBuf(mapName string, eventsChan chan []byte) (*RingBuffe
return nil, fmt.Errorf("max ring buffers reached")
}

rb := C.libbpfgo_init_ring_buf(bpfMap.fd, C.uintptr_t(slot))
rb := C.libbpfgo_init_ring_buf(C.int(bpfMap.FileDescriptor()), C.uintptr_t(slot))
if rb == nil {
return nil, fmt.Errorf("failed to initialize ring buffer")
}
Expand Down Expand Up @@ -1264,7 +1305,7 @@ func (m *Module) InitPerfBuf(mapName string, eventsChan chan []byte, lostChan ch
return nil, fmt.Errorf("max number of ring/perf buffers reached")
}

pb := C.libbpfgo_init_perf_buf(bpfMap.fd, C.int(pageCnt), C.uintptr_t(slot))
pb := C.libbpfgo_init_perf_buf(C.int(bpfMap.FileDescriptor()), C.int(pageCnt), C.uintptr_t(slot))
if pb == nil {
eventChannels.remove(uint(slot))
return nil, fmt.Errorf("failed to initialize perf buffer")
Expand Down
14 changes: 14 additions & 0 deletions libbpfgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,18 @@ struct bpf_object_open_opts *libbpfgo_bpf_object_open_opts_new(const char *btf_f
const char *bpf_obj_name);
void libbpfgo_bpf_object_open_opts_free(struct bpf_object_open_opts *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);
void libbpfgo_bpf_map_create_opts_free(struct bpf_map_create_opts *opts);

struct bpf_map_batch_opts *libbpfgo_bpf_map_batch_opts_new(__u64 elem_flags, __u64 flags);
void libbpfgo_bpf_map_batch_opts_free(struct bpf_map_batch_opts *opts);

#endif
186 changes: 186 additions & 0 deletions map-common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package libbpfgo

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

import (
"fmt"
"syscall"
)

//
// MapType
//

type MapType uint32

const (
MapTypeUnspec MapType = iota
MapTypeHash
MapTypeArray
MapTypeProgArray
MapTypePerfEventArray
MapTypePerCPUHash
MapTypePerCPUArray
MapTypeStackTrace
MapTypeCgroupArray
MapTypeLRUHash
MapTypeLRUPerCPUHash
MapTypeLPMTrie
MapTypeArrayOfMaps
MapTypeHashOfMaps
MapTypeDevMap
MapTypeSockMap
MapTypeCPUMap
MapTypeXSKMap
MapTypeSockHash
MapTypeCgroupStorage
MapTypeReusePortSockArray
MapTypePerCPUCgroupStorage
MapTypeQueue
MapTypeStack
MapTypeSKStorage
MapTypeDevmapHash
MapTypeStructOps
MapTypeRingbuf
MapTypeInodeStorage
MapTypeTaskStorage
MapTypeBloomFilter
)

var mapTypeToString = map[MapType]string{
MapTypeUnspec: "BPF_MAP_TYPE_UNSPEC",
MapTypeHash: "BPF_MAP_TYPE_HASH",
MapTypeArray: "BPF_MAP_TYPE_ARRAY",
MapTypeProgArray: "BPF_MAP_TYPE_PROG_ARRAY",
MapTypePerfEventArray: "BPF_MAP_TYPE_PERF_EVENT_ARRAY",
MapTypePerCPUHash: "BPF_MAP_TYPE_PERCPU_HASH",
MapTypePerCPUArray: "BPF_MAP_TYPE_PERCPU_ARRAY",
MapTypeStackTrace: "BPF_MAP_TYPE_STACK_TRACE",
MapTypeCgroupArray: "BPF_MAP_TYPE_CGROUP_ARRAY",
MapTypeLRUHash: "BPF_MAP_TYPE_LRU_HASH",
MapTypeLRUPerCPUHash: "BPF_MAP_TYPE_LRU_PERCPU_HASH",
MapTypeLPMTrie: "BPF_MAP_TYPE_LPM_TRIE",
MapTypeArrayOfMaps: "BPF_MAP_TYPE_ARRAY_OF_MAPS",
MapTypeHashOfMaps: "BPF_MAP_TYPE_HASH_OF_MAPS",
MapTypeDevMap: "BPF_MAP_TYPE_DEVMAP",
MapTypeSockMap: "BPF_MAP_TYPE_SOCKMAP",
MapTypeCPUMap: "BPF_MAP_TYPE_CPUMAP",
MapTypeXSKMap: "BPF_MAP_TYPE_XSKMAP",
MapTypeSockHash: "BPF_MAP_TYPE_SOCKHASH",
MapTypeCgroupStorage: "BPF_MAP_TYPE_CGROUP_STORAGE",
MapTypeReusePortSockArray: "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY",
MapTypePerCPUCgroupStorage: "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE",
MapTypeQueue: "BPF_MAP_TYPE_QUEUE",
MapTypeStack: "BPF_MAP_TYPE_STACK",
MapTypeSKStorage: "BPF_MAP_TYPE_SK_STORAGE",
MapTypeDevmapHash: "BPF_MAP_TYPE_DEVMAP_HASH",
MapTypeStructOps: "BPF_MAP_TYPE_STRUCT_OPS",
MapTypeRingbuf: "BPF_MAP_TYPE_RINGBUF",
MapTypeInodeStorage: "BPF_MAP_TYPE_INODE_STORAGE",
MapTypeTaskStorage: "BPF_MAP_TYPE_TASK_STORAGE",
MapTypeBloomFilter: "BPF_MAP_TYPE_BLOOM_FILTER",
}

func (t MapType) String() string {
return mapTypeToString[t]
}

//
// MapFlag
//

type MapFlag uint32

const (
MapFlagUpdateAny MapFlag = iota // create new element or update existing
MapFlagUpdateNoExist // create new element if it didn't exist
MapFlagUpdateExist // update existing element
MapFlagFLock // spin_lock-ed map_lookup/map_update
)

//
// BPFMapInfo
//

// BPFMapInfo mirrors the C structure bpf_map_info.
type BPFMapInfo struct {
Type MapType
ID uint32
KeySize uint32
ValueSize uint32
MaxEntries uint32
MapFlags uint32
Name string
IfIndex uint32
BTFVmlinuxValueTypeID uint32
NetnsDev uint64
NetnsIno uint64
BTFID uint32
BTFKeyTypeID uint32
BTFValueTypeID uint32
MapExtra uint64
}

// GetMapInfoByFD returns the BPFMapInfo for the map with the given file descriptor.
func GetMapInfoByFD(fd int) (*BPFMapInfo, error) {
var info C.struct_bpf_map_info
var infoLen C.uint = C.uint(C.sizeof_struct_bpf_map_info)

retC := C.bpf_map_get_info_by_fd(C.int(fd), &info, &infoLen)
if retC < 0 {
return nil, fmt.Errorf("failed to get map info for fd %d: %w", fd, syscall.Errno(-retC))
}

return &BPFMapInfo{
Type: MapType(uint32(info._type)),
ID: uint32(info.id),
KeySize: uint32(info.key_size),
ValueSize: uint32(info.value_size),
MaxEntries: uint32(info.max_entries),
MapFlags: uint32(info.map_flags),
Name: C.GoString(&info.name[0]),
IfIndex: uint32(info.ifindex),
BTFVmlinuxValueTypeID: uint32(info.btf_vmlinux_value_type_id),
NetnsDev: uint64(info.netns_dev),
NetnsIno: uint64(info.netns_ino),
BTFID: uint32(info.btf_id),
BTFKeyTypeID: uint32(info.btf_key_type_id),
BTFValueTypeID: uint32(info.btf_value_type_id),
MapExtra: uint64(info.map_extra),
}, nil
}

//
// Map misc internal
//

// calcMapValueSize calculates the size of the value for a map.
// For per-CPU maps, it is calculated based on the number of possible CPUs.
func calcMapValueSize(valueSize int, mapType MapType) (int, error) {
if valueSize <= 0 {
return 0, fmt.Errorf("value size must be greater than 0")
}

switch mapType {
case MapTypePerCPUArray,
MapTypePerCPUHash,
MapTypeLRUPerCPUHash,
MapTypePerCPUCgroupStorage:
// per-CPU maps have a value size calculated using a round-up of the
// element size multiplied by the number of possible CPUs.
elemSize := roundUp(uint64(valueSize), 8)
numCPU, err := NumPossibleCPUs()
if err != nil {
return 0, err
}

return int(elemSize) * numCPU, nil
default:
// For other maps, the value size does not change.
return valueSize, nil
}
}
Loading

0 comments on commit ba052a5

Please sign in to comment.