diff --git a/map-low.go b/map-low.go index ea1542f8..3a7cbac2 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) { -// } +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) @@ -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 diff --git a/map.go b/map.go index 6706fd15..1fdd8c36 100644 --- a/map.go +++ b/map.go @@ -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. @@ -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) diff --git a/selftest/map-batch/main.go b/selftest/map-batch/main.go index 555ce878..54f27e35 100644 --- a/selftest/map-batch/main.go +++ b/selftest/map-batch/main.go @@ -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 {