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

Add new methods to Map API #419

Merged
merged 2 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 2 additions & 6 deletions map-common.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,9 @@ func GetMapInfoByFD(fd int) (*BPFMapInfo, error) {
}, nil
}

//
// Map misc internal
//

// calcMapValueSize calculates the size of the value for a map.
// 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) {
func CalcMapValueSize(valueSize int, mapType MapType) (int, error) {
if valueSize <= 0 {
return 0, fmt.Errorf("value size must be greater than 0")
}
Expand Down
94 changes: 85 additions & 9 deletions map-low.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func (m *BPFMapLow) GetValue(key unsafe.Pointer) ([]byte, error) {
}

func (m *BPFMapLow) GetValueFlags(key unsafe.Pointer, flags MapFlag) ([]byte, error) {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}
Expand All @@ -279,9 +279,76 @@ func (m *BPFMapLow) GetValueFlags(key unsafe.Pointer, flags MapFlag) ([]byte, er
return value, nil
}

// TODO: implement `bpf_map__lookup_and_delete_elem`
// func (m *BPFMapLow) GetValueAndDeleteKey(key unsafe.Pointer) ([]byte, error) {
// }
func (m *BPFMapLow) LookupAndDeleteElem(
key unsafe.Pointer,
value unsafe.Pointer,
) error {
retC := C.bpf_map_lookup_and_delete_elem(
C.int(m.FileDescriptor()),
key,
value,
)
if retC < 0 {
return fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return nil
}

func (m *BPFMapLow) LookupAndDeleteElemFlags(
key unsafe.Pointer,
value unsafe.Pointer,
flags MapFlag,
) error {
retC := C.bpf_map_lookup_and_delete_elem_flags(
C.int(m.FileDescriptor()),
key,
value,
C.ulonglong(flags),
)
if retC < 0 {
return fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return nil
}

func (m *BPFMapLow) GetValueAndDeleteKey(key unsafe.Pointer) ([]byte, error) {
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}

value := make([]byte, valueSize)
err = m.LookupAndDeleteElem(
key,
unsafe.Pointer(&value[0]),
)
if err != nil {
return nil, err
}

return value, nil
}

func (m *BPFMapLow) GetValueAndDeleteKeyFlags(key unsafe.Pointer, flags MapFlag) ([]byte, error) {
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}

value := make([]byte, valueSize)
err = m.LookupAndDeleteElemFlags(
key,
unsafe.Pointer(&value[0]),
flags,
)
if err != nil {
return nil, err
}

return value, nil
}

func (m *BPFMapLow) Update(key, value unsafe.Pointer) error {
return m.UpdateValueFlags(key, value, MapFlagUpdateAny)
Expand Down Expand Up @@ -310,9 +377,18 @@ func (m *BPFMapLow) DeleteKey(key unsafe.Pointer) error {
return nil
}

// TODO: implement `bpf_map__get_next_key`
// func (m *BPFMapLow) GetNextKey(key unsafe.Pointer) (unsafe.Pointer, error) {
// }
func (m *BPFMapLow) GetNextKey(key unsafe.Pointer, nextKey unsafe.Pointer) error {
retC := C.bpf_map_get_next_key(
C.int(m.FileDescriptor()),
key,
nextKey,
)
if retC < 0 {
return fmt.Errorf("failed to get next key in map %s: %w", m.Name(), syscall.Errno(-retC))
}

return nil
}

//
// BPFMapLow Batch Operations
Expand All @@ -321,7 +397,7 @@ func (m *BPFMapLow) DeleteKey(key unsafe.Pointer) error {
// GetValueBatch gets the values with the given keys from the map.
// It returns the values and the number of read elements.
func (m *BPFMapLow) GetValueBatch(keys, startKey, nextKey unsafe.Pointer, count uint32) ([][]byte, uint32, error) {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, 0, fmt.Errorf("map %s %w", m.Name(), err)
}
Expand Down Expand Up @@ -368,7 +444,7 @@ func (m *BPFMapLow) GetValueBatch(keys, startKey, nextKey unsafe.Pointer, count
// deletes them.
// It returns the values and the number of deleted elements.
func (m *BPFMapLow) GetValueAndDeleteBatch(keys, startKey, nextKey unsafe.Pointer, count uint32) ([][]byte, uint32, error) {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, 0, fmt.Errorf("map %s %w", m.Name(), err)
}
Expand Down
75 changes: 65 additions & 10 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (m *BPFMap) MapExtra() uint64 {
// }

func (m *BPFMap) InitialValue() ([]byte, error) {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}
Expand All @@ -227,7 +227,7 @@ func (m *BPFMap) InitialValue() ([]byte, error) {
}

func (m *BPFMap) SetInitialValue(value unsafe.Pointer) error {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return fmt.Errorf("map %s %w", m.Name(), err)
}
Expand Down Expand Up @@ -384,7 +384,7 @@ func (m *BPFMap) GetValue(key unsafe.Pointer) ([]byte, error) {
}

func (m *BPFMap) GetValueFlags(key unsafe.Pointer, flags MapFlag) ([]byte, error) {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}
Expand All @@ -405,9 +405,53 @@ func (m *BPFMap) GetValueFlags(key unsafe.Pointer, flags MapFlag) ([]byte, error
return value, nil
}

// TODO: implement `bpf_map__lookup_and_delete_elem` wrapper
// func (m *BPFMap) GetValueAndDeleteKey(key unsafe.Pointer) ([]byte, error) {
// }
// LookupAndDeleteElem stores the value associated with a given key into the
// provided unsafe.Pointer and deletes the key from the BPFMap.
func (m *BPFMap) LookupAndDeleteElem(
key unsafe.Pointer,
value unsafe.Pointer,
valueSize uint64,
flags MapFlag,
) error {
retC := C.bpf_map__lookup_and_delete_elem(
m.bpfMap,
key,
C.ulong(m.KeySize()),
value,
C.ulong(valueSize),
C.ulonglong(flags),
)
if retC < 0 {
return fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return nil
}

// GetValueAndDeleteKey retrieves the value associated with a given key
// and delete the key in the BPFMap.
// It returns the value as a slice of bytes.
func (m *BPFMap) GetValueAndDeleteKey(key unsafe.Pointer) ([]byte, error) {
return m.GetValueAndDeleteKeyFlags(key, MapFlagUpdateAny)
}

// GetValueAndDeleteKeyFlags retrieves the value associated with a given key
// and delete the key in the BPFMap, with the specified flags.
// It returns the value as a slice of bytes.
func (m *BPFMap) GetValueAndDeleteKeyFlags(key unsafe.Pointer, flags MapFlag) ([]byte, error) {
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return nil, fmt.Errorf("map %s %w", m.Name(), err)
}

value := make([]byte, valueSize)
err = m.LookupAndDeleteElem(key, unsafe.Pointer(&value[0]), uint64(valueSize), flags)
if err != nil {
return nil, err
}

return value, nil
}

// Deprecated: use BPFMap.GetValue() or BPFMap.GetValueFlags() instead, since
// they already calculate the value size for per-cpu maps.
Expand Down Expand Up @@ -442,7 +486,7 @@ func (m *BPFMap) Update(key, value unsafe.Pointer) error {
}

func (m *BPFMap) UpdateValueFlags(key, value unsafe.Pointer, flags MapFlag) error {
valueSize, err := calcMapValueSize(m.ValueSize(), m.Type())
valueSize, err := CalcMapValueSize(m.ValueSize(), m.Type())
if err != nil {
return fmt.Errorf("map %s %w", m.Name(), err)
}
Expand Down Expand Up @@ -480,9 +524,20 @@ func (m *BPFMap) DeleteKey(key unsafe.Pointer) error {
return nil
}

// TODO: implement `bpf_map__get_next_key` wrapper
// func (m *BPFMap) GetNextKey(key unsafe.Pointer) (unsafe.Pointer, error) {
// }
// GetNextKey allows to iterate BPF map keys by fetching next key that follows current key.
func (m *BPFMap) GetNextKey(key unsafe.Pointer, nextKey unsafe.Pointer) error {
retC := C.bpf_map__get_next_key(
m.bpfMap,
key,
nextKey,
C.ulong(m.KeySize()),
)
if retC < 0 {
return fmt.Errorf("failed to get next key %d in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return nil
}

//
// BPFMap Batch Operations (low-level API)
Expand Down
67 changes: 67 additions & 0 deletions selftest/map-batch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,73 @@ func main() {
if count != uint32(fewer) {
log.Fatalf("testerMap.DeleteKeyBatch failed: count=%d", count)
}

// map contains only 1 key-value pair.

// Re-add deleted entries.
_, err = testerMap.UpdateBatch(
unsafe.Pointer(&keys[0]),
unsafe.Pointer(&values[0]),
uint32(len(keys)),
)
if err != nil {
log.Fatal(err)
}

//
// GetNextKey
//

// Populate the map again.
_, err = testerMap.UpdateBatch(
unsafe.Pointer(&keys[0]),
unsafe.Pointer(&values[0]),
uint32(len(keys)),
)
if err != nil {
log.Fatal(err)
}

// Test GetNextKey.
key := uint32(0)
keyPtr := unsafe.Pointer(&key)
keyCnt := 0
for {
err := testerMap.GetNextKey(keyPtr, keyPtr)
if err != nil {
if !errors.Is(err, syscall.ENOENT) {
log.Fatalf("testerMap.GetNextKey failed: err=%v", err)
}
break
}
keyCnt++
}
if keyCnt != len(keys) {
log.Fatalf("testerMap.GetNextKey failed: count=%d", keyCnt)
}

//
// GetValueAndDeleteKey
//

// Test GetValueAndDeleteKey.
for i, key := range keys {
val, err := testerMap.GetValueAndDeleteKey(unsafe.Pointer(&key))
if err != nil {
log.Fatalf("testerMap.GetValueAndDelete failed: err=%v", err)
}

if endian().Uint32(val) != values[i] {
log.Fatalf("testerMpa.GetValueAndDetele failed: val=%d", endian().Uint32(val))
}
}

// Check if all keys are deleted.
key = 0
err = testerMap.GetNextKey(keyPtr, keyPtr)
if !errors.Is(err, syscall.ENOENT) {
log.Fatalf("testerMap.GetValueAndDeleteKey failed: err=%v", err)
}
}

func endian() binary.ByteOrder {
Expand Down
Loading