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

feat(map): Add GetNextKey and GetValueAndDeleteKey #411

Closed
wants to merge 3 commits into from
Closed
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
58 changes: 52 additions & 6 deletions map-low.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,44 @@ 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) 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)
retC := C.bpf_map_lookup_and_delete_elem(
C.int(m.FileDescriptor()),
key,
unsafe.Pointer(&value[0]),
)
if retC < 0 {
return nil, fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

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)
retC := C.bpf_map_lookup_and_delete_elem_flags(
C.int(m.FileDescriptor()),
key,
unsafe.Pointer(&value[0]),
C.ulonglong(flags),
)
if retC < 0 {
return nil, fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return value, nil
}

func (m *BPFMapLow) Update(key, value unsafe.Pointer) error {
return m.UpdateValueFlags(key, value, MapFlagUpdateAny)
Expand Down Expand Up @@ -310,9 +345,20 @@ 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) (unsafe.Pointer, error) {
next := make([]byte, m.KeySize())
nextPtr := unsafe.Pointer(&next[0])
retC := C.bpf_map_get_next_key(
C.int(m.FileDescriptor()),
key,
nextPtr,
)
if retC < 0 {
return nil, fmt.Errorf("failed to get next key in map %s: %w", m.Name(), syscall.Errno(-retC))
}

return nextPtr, nil
}

//
// BPFMapLow Batch Operations
Expand Down
51 changes: 45 additions & 6 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,35 @@ 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) {
// }
// GetValueAndDeleteKey retrieves the value associated with a given key
// and delete the key in the BPFMap.
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.
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)
retC := C.bpf_map__lookup_and_delete_elem(
m.bpfMap,
key,
C.ulong(m.KeySize()),
unsafe.Pointer(&value[0]),
C.ulong(valueSize),
C.ulonglong(flags),
)
if retC < 0 {
return nil, fmt.Errorf("failed to lookup and delete value %v in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

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 @@ -480,9 +506,22 @@ 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 retrieves the next key in the BPFMap after the given key.
func (m *BPFMap) GetNextKey(key unsafe.Pointer) (unsafe.Pointer, error) {
next := make([]byte, m.KeySize())
nextPtr := unsafe.Pointer(&next[0])
retC := C.bpf_map__get_next_key(
m.bpfMap,
key,
nextPtr,
C.ulong(m.KeySize()),
)
if retC < 0 {
return nil, fmt.Errorf("failed to get next key %d in map %s: %w", key, m.Name(), syscall.Errno(-retC))
}

return nextPtr, nil
}

//
// BPFMap Batch Operations (low-level API)
Expand Down
70 changes: 70 additions & 0 deletions selftest/map-batch/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,76 @@ 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.
keyPtr := unsafe.Pointer(nil)
keyCnt := 0
for {
nextKey, err := testerMap.GetNextKey(keyPtr)
keyPtr = nextKey
if nextKey != nil {
keyCnt++
continue
}

if !errors.Is(err, syscall.ENOENT) {
log.Fatalf("testerMap.GetNextKey failed: err=%v", err)
}

break
}
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.
keyPtr = unsafe.Pointer(nil)
_, err = testerMap.GetNextKey(keyPtr)
if !errors.Is(err, syscall.ENOENT) {
log.Fatalf("testerMap.GetValueAndDeleteKey failed: err=%v", err)
}
}

func endian() binary.ByteOrder {
Expand Down