From 72f088591fbe48a0e4da1cc13b4549b23477c74e Mon Sep 17 00:00:00 2001 From: Tao Chen Date: Wed, 27 Mar 2024 23:30:58 +0800 Subject: [PATCH] feat(map): Add GetNextKey and GetValueAndDeleteKey Add GetNextKey() and GetValueAndDeleteKey() for map and map-low. Signed-off-by: Tao Chen --- map-low.go | 56 ++++++++++++++++++++++++++++++++---- map.go | 42 +++++++++++++++++++++++---- selftest/map-batch/main.go | 58 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/map-low.go b/map-low.go index ea1542f8..9a59b3ea 100644 --- a/map-low.go +++ b/map-low.go @@ -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) { -// } +// GetValueAndDeleteKeyFlags gets the value with the given key and flags, delete the key from the map. +// It returns the value and delete the given key. +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) + valuePtr := unsafe.Pointer(&(value[0])) + retC := C.bpf_map_lookup_and_delete_elem_flags( + C.int(m.FileDescriptor()), + key, + valuePtr, + 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 +} + +// GetValueAndDeleteKey gets the value with the given key and delete the key from the map. +// It returns the value and delete the given key. +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) + valuePtr := unsafe.Pointer(&(value[0])) + retC := C.bpf_map_lookup_and_delete_elem(C.int(m.FileDescriptor()), key, valuePtr) + 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) @@ -310,9 +345,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) { -// } +// GetNextKey gets the next key with the given key from the map. +// It returns the next key. +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 diff --git a/map.go b/map.go index 6706fd15..ee7ad35c 100644 --- a/map.go +++ b/map.go @@ -405,9 +405,32 @@ 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) { -// } +// GetValue 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) +} + +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) + valuePtr := unsafe.Pointer(&(value[0])) + + retC := C.bpf_map__lookup_and_delete_elem( + m.bpfMap, key, + C.ulong(m.KeySize()), + valuePtr, + 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. @@ -480,9 +503,16 @@ 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 with a given key from the BPFMap. +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) diff --git a/selftest/map-batch/main.go b/selftest/map-batch/main.go index 555ce878..18e51ca7 100644 --- a/selftest/map-batch/main.go +++ b/selftest/map-batch/main.go @@ -247,6 +247,64 @@ func main() { if count != uint32(fewer) { log.Fatalf("testerMap.DeleteKeyBatch failed: count=%d", count) } + + // + // GetNextKey + // + + // Test get next key + _, err = testerMap.UpdateBatch( + unsafe.Pointer(&keys[0]), + unsafe.Pointer(&values[0]), + uint32(len(keys)), + ) + if err != nil { + log.Fatal(err) + } + + key := unsafe.Pointer(nil) + keyCnt := 0 + for { + nextKey, err := testerMap.GetNextKey(key) + key = nextKey + if err != nil { + break + } + keyCnt++ + } + if keyCnt != len(keys) { + log.Fatalf("testerMap.GetNextKey failed: count=%d", keyCnt) + } + + // + // GetValueAndDelete + // + + // Test get value and delete + 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 all keys deleted + key = unsafe.Pointer(nil) + keyCnt = 0 + for { + nextKey, err := testerMap.GetNextKey(key) + key = nextKey + if err != nil { + break + } + keyCnt++ + } + if keyCnt != 0 { + log.Fatalf("testerMap.GetValueAndDeleteKey failed: count=%d", keyCnt) + } } func endian() binary.ByteOrder {