From bb133708f167d066285b202043b5cd7f0adc4c75 Mon Sep 17 00:00:00 2001 From: Nate Sweet Date: Tue, 23 Feb 2021 10:31:28 +0000 Subject: [PATCH 1/4] bpf: Add Batch Methods As of kernel v5.6 batch methods allow for the fast lookup, deletion, and updating of bpf maps so that the syscall overhead (repeatedly calling into any of these methods) can be avoided. The batch methods are as follows: * BatchUpdate * BatchLookup * BatchLookupAndDelete * BatchDelete Only the "array" and "hash" types currently support batch operations, and the "array" type does not support batch deletion. Tests are in place to test every scenario and helper functions have been written to catch errors that normally the kernel would give to helpful to users of the library. Signed-off-by: Nate Sweet --- internal/unix/types_linux.go | 23 +++-- internal/unix/types_other.go | 23 +++-- map.go | 161 ++++++++++++++++++++++++++++- map_test.go | 192 +++++++++++++++++++++++++++++++++++ marshalers.go | 6 +- syscalls.go | 68 ++++++++++++- syscalls_test.go | 4 + types.go | 10 ++ 8 files changed, 460 insertions(+), 27 deletions(-) diff --git a/internal/unix/types_linux.go b/internal/unix/types_linux.go index 215f67118..86d2a10f9 100644 --- a/internal/unix/types_linux.go +++ b/internal/unix/types_linux.go @@ -10,16 +10,19 @@ import ( ) const ( - ENOENT = linux.ENOENT - EEXIST = linux.EEXIST - EAGAIN = linux.EAGAIN - ENOSPC = linux.ENOSPC - EINVAL = linux.EINVAL - EPOLLIN = linux.EPOLLIN - EINTR = linux.EINTR - EPERM = linux.EPERM - ESRCH = linux.ESRCH - ENODEV = linux.ENODEV + ENOENT = linux.ENOENT + EEXIST = linux.EEXIST + EAGAIN = linux.EAGAIN + ENOSPC = linux.ENOSPC + EINVAL = linux.EINVAL + EPOLLIN = linux.EPOLLIN + EINTR = linux.EINTR + EPERM = linux.EPERM + ESRCH = linux.ESRCH + ENODEV = linux.ENODEV + // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP + ENOTSUPP = syscall.Errno(0x20c) + EBADF = linux.EBADF BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE diff --git a/internal/unix/types_other.go b/internal/unix/types_other.go index 529800d59..8c291796a 100644 --- a/internal/unix/types_other.go +++ b/internal/unix/types_other.go @@ -11,16 +11,19 @@ import ( var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) const ( - ENOENT = syscall.ENOENT - EEXIST = syscall.EEXIST - EAGAIN = syscall.EAGAIN - ENOSPC = syscall.ENOSPC - EINVAL = syscall.EINVAL - EINTR = syscall.EINTR - EPERM = syscall.EPERM - ESRCH = syscall.ESRCH - ENODEV = syscall.ENODEV - EBADF = syscall.Errno(0) + ENOENT = syscall.ENOENT + EEXIST = syscall.EEXIST + EAGAIN = syscall.EAGAIN + ENOSPC = syscall.ENOSPC + EINVAL = syscall.EINVAL + EINTR = syscall.EINTR + EPERM = syscall.EPERM + ESRCH = syscall.ESRCH + ENODEV = syscall.ENODEV + EBADF = syscall.Errno(0) + // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP + ENOTSUPP = syscall.Errno(0x20c) + BPF_F_NO_PREALLOC = 0 BPF_F_NUMA_NODE = 0 BPF_F_RDONLY_PROG = 0 diff --git a/map.go b/map.go index 658be27f6..92cd0ce94 100644 --- a/map.go +++ b/map.go @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + "reflect" "strings" "github.com/cilium/ebpf/internal" @@ -418,7 +419,6 @@ func (m *Map) Info() (*MapInfo, error) { // Returns an error if the key doesn't exist, see ErrKeyNotExist. func (m *Map) Lookup(key, valueOut interface{}) error { valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) - if err := m.lookup(key, valuePtr); err != nil { return err } @@ -499,7 +499,7 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { return fmt.Errorf("can't marshal key: %w", err) } - valuePtr, err := m.marshalValue(value) + valuePtr, err := m.marshalValue(value, false) if err != nil { return fmt.Errorf("can't marshal value: %w", err) } @@ -582,6 +582,161 @@ func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { return nil } +// BatchLookup looks up many elements in a map at once +// with the startKey being the first element to start +// from (nil if starting from the beginning is desired). +// "keysOut" and "valuesOut" must be of type slice, a pointer +// to a slice or buffer will not work. +// "prevKey" is the key to start the batch lookup from, it will +// *not* be included in the results. +// +// ErrKeyNotExist is returned when the batch lookup has reached +// the end of all possible results, even when partial results +// are returned. It should be used to evaluate when lookup is "done". +func (m *Map) BatchLookup(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + return m.batchLookup(internal.BPF_MAP_LOOKUP_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) +} + +// BatchLookupAndDelete looks up many elements in a map at once +// with the startKey being the first element to start +// from (nil if starting from the beginning is desired). +// It then deletes all those elements. +// "keysOut" and "valuesOut" must be of type slice, a pointer +// to a slice or buffer will not work. +// "prevKey" is the key to start the batch lookup from, it will +// *not* be included in the results. +// +// ErrKeyNotExist is returned when the batch lookup has reached +// the end of all possible results, even when partial results +// are returned. It should be used to evaluate when lookup is "done". +func (m *Map) BatchLookupAndDelete(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + return m.batchLookup(internal.BPF_MAP_LOOKUP_AND_DELETE_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) +} + +func (m *Map) batchLookup(cmd internal.BPFCmd, startKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keysOut) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + valuesValue := reflect.ValueOf(valuesOut) + if valuesValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("valuesOut must be a slice") + } + count := keysValue.Len() + if count != valuesValue.Len() { + return 0, fmt.Errorf("keysOut and valuesOut must be the same length") + } + keyBuf := make([]byte, count*int(m.keySize)) + keyPtr := internal.NewSlicePointer(keyBuf) + valueBuf := make([]byte, count*int(m.fullValueSize)) + valuePtr := internal.NewSlicePointer(valueBuf) + + var ( + startPtr internal.Pointer + err error + retErr error + ) + if startKey != nil { + startPtr, err = marshalPtr(startKey, int(m.keySize)) + if err != nil { + return 0, err + } + } + nextPtr, nextBuf := makeBuffer(nextKeyOut, int(m.keySize)) + + ct, err := bpfMapBatch(cmd, m.fd, startPtr, nextPtr, keyPtr, valuePtr, uint32(count), opts) + fmt.Println(err) + if err != nil { + if !errors.Is(err, ErrKeyNotExist) { + return 0, err + } + retErr = ErrKeyNotExist + } + + err = m.unmarshalKey(nextKeyOut, nextBuf) + if err != nil { + return 0, err + } + err = unmarshalBytes(keysOut, keyBuf) + if err != nil { + return 0, err + } + err = unmarshalBytes(valuesOut, valueBuf) + if err != nil { + retErr = err + } + return int(ct), retErr +} + +// BatchUpdate updates the map with multiple keys and values +// simultaneously. +// "keys" and "values" must be of type slice, a pointer +// to a slice or buffer will not work. +func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keys) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + valuesValue := reflect.ValueOf(values) + if valuesValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("values must be a slice") + } + var ( + count = keysValue.Len() + valuePtr internal.Pointer + err error + ) + if count != valuesValue.Len() { + return 0, fmt.Errorf("keys and values must be the same length") + } + keyPtr, err := marshalPtr(keys, count*int(m.keySize)) + if err != nil { + return 0, err + } + valuePtr, err = marshalPtr(values, count*int(m.valueSize)) + if err != nil { + return 0, err + } + var nilPtr internal.Pointer + ct, err := bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, valuePtr, uint32(count), opts) + return int(ct), err +} + +// BatchDelete batch deletes entries in the map by keys. +// "keys" must be of type slice, a pointer to a slice or buffer will not work. +func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keys) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + count := keysValue.Len() + keyPtr, err := marshalPtr(keys, count*int(m.keySize)) + if err != nil { + return 0, fmt.Errorf("cannot marshal keys: %v", err) + } + var nilPtr internal.Pointer + ct, err := bpfMapBatch(internal.BPF_MAP_DELETE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, nilPtr, uint32(count), opts) + return int(ct), err +} + // Iterate traverses a map. // // It's safe to create multiple iterators at the same time. @@ -745,7 +900,7 @@ func (m *Map) unmarshalKey(data interface{}, buf []byte) error { return unmarshalBytes(data, buf) } -func (m *Map) marshalValue(data interface{}) (internal.Pointer, error) { +func (m *Map) marshalValue(data interface{}, batch bool) (internal.Pointer, error) { if m.typ.hasPerCPUValue() { return marshalPerCPUValue(data, int(m.valueSize)) } diff --git a/map_test.go b/map_test.go index bf4d0f025..6fab94fe2 100644 --- a/map_test.go +++ b/map_test.go @@ -7,6 +7,7 @@ import ( "math" "os" "path/filepath" + "reflect" "sort" "strings" "testing" @@ -81,6 +82,197 @@ func TestMap(t *testing.T) { } } +func TestBatchAPIArray(t *testing.T) { + if err := haveBatchAPI(); err != nil { + t.Skipf("batch api not available: %v", err) + } + m, err := NewMap(&MapSpec{ + Type: Array, + KeySize: 4, + ValueSize: 4, + MaxEntries: 2, + }) + if err != nil { + t.Fatal(err) + } + defer m.Close() + + var ( + nextKey uint32 + keys = []uint32{0, 1} + values = []uint32{42, 4242} + lookupKeys = make([]uint32, 2) + lookupValues = make([]uint32, 2) + deleteKeys = make([]uint32, 2) + deleteValues = make([]uint32, 2) + ) + + count, err := m.BatchUpdate(keys, values, nil) + if err != nil { + t.Fatalf("BatchUpdate: %v", err) + } + if count != len(keys) { + t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) + } + + var v uint32 + if err := m.Lookup(uint32(0), &v); err != nil { + t.Fatal("Can't lookup 0:", err) + } + if v != 42 { + t.Error("Want value 42, got", v) + } + + count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil) + if err != nil { + t.Fatalf("BatchLookup: %v", err) + } + if count != len(lookupKeys) { + t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys)) + } + if nextKey != lookupKeys[1] { + t.Fatalf("BatchLookup: expected nextKey, %d, to be the same as the lastKey returned, %d", nextKey, lookupKeys[1]) + } + if !reflect.DeepEqual(keys, lookupKeys) { + t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys) + } + if !reflect.DeepEqual(values, lookupValues) { + t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues) + } + + _, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil) + if !errors.Is(err, ErrNotSupported) { + t.Fatalf("BatchLookUpDelete: expected error %v, but got %v", ErrNotSupported, err) + } +} + +func TestBatchAPIHash(t *testing.T) { + if err := haveBatchAPI(); err != nil { + t.Skipf("batch api not available: %v", err) + } + m, err := NewMap(&MapSpec{ + Type: Hash, + KeySize: 4, + ValueSize: 4, + MaxEntries: 10, + }) + if err != nil { + t.Fatal(err) + } + defer m.Close() + + var ( + nextKey uint32 + keys = []uint32{0, 1} + values = []uint32{42, 4242} + lookupKeys = make([]uint32, 2) + lookupValues = make([]uint32, 2) + deleteKeys = make([]uint32, 2) + deleteValues = make([]uint32, 2) + ) + + count, err := m.BatchUpdate(keys, values, nil) + if err != nil { + t.Fatalf("BatchUpdate: %v", err) + } + if count != len(keys) { + t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) + } + + var v uint32 + if err := m.Lookup(uint32(0), &v); err != nil { + t.Fatal("Can't lookup 0:", err) + } + if v != 42 { + t.Error("Want value 42, got", v) + } + + count, err = m.BatchLookup(nil, &nextKey, lookupKeys, lookupValues, nil) + if !errors.Is(err, ErrKeyNotExist) { + t.Fatalf("BatchLookup: expected %v got %v", ErrKeyNotExist, err) + } + if count != len(lookupKeys) { + t.Fatalf("BatchLookup: returned %d results, expected %d", count, len(lookupKeys)) + } + sort.Slice(lookupKeys, func(i, j int) bool { return lookupKeys[i] < lookupKeys[j] }) + if !reflect.DeepEqual(keys, lookupKeys) { + t.Errorf("BatchUpdate and BatchLookup keys disagree: %v %v", keys, lookupKeys) + } + sort.Slice(lookupValues, func(i, j int) bool { return lookupValues[i] < lookupValues[j] }) + if !reflect.DeepEqual(values, lookupValues) { + t.Errorf("BatchUpdate and BatchLookup values disagree: %v %v", values, lookupValues) + } + + count, err = m.BatchLookupAndDelete(nil, &nextKey, deleteKeys, deleteValues, nil) + if !errors.Is(err, ErrKeyNotExist) { + t.Fatalf("BatchLookupAndDelete: expected %v got %v", ErrKeyNotExist, err) + } + if count != len(deleteKeys) { + t.Fatalf("BatchLookupAndDelete: returned %d results, expected %d", count, len(deleteKeys)) + } + sort.Slice(deleteKeys, func(i, j int) bool { return deleteKeys[i] < deleteKeys[j] }) + if !reflect.DeepEqual(keys, deleteKeys) { + t.Errorf("BatchUpdate and BatchLookupAndDelete keys disagree: %v %v", keys, deleteKeys) + } + sort.Slice(deleteValues, func(i, j int) bool { return deleteValues[i] < deleteValues[j] }) + if !reflect.DeepEqual(values, deleteValues) { + t.Errorf("BatchUpdate and BatchLookupAndDelete values disagree: %v %v", values, deleteValues) + } + + if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) { + t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err) + } +} + +func TestBatchAPIMapDelete(t *testing.T) { + if err := haveBatchAPI(); err != nil { + t.Skipf("batch api not available: %v", err) + } + m, err := NewMap(&MapSpec{ + Type: Hash, + KeySize: 4, + ValueSize: 4, + MaxEntries: 10, + }) + if err != nil { + t.Fatal(err) + } + defer m.Close() + + var ( + keys = []uint32{0, 1} + values = []uint32{42, 4242} + ) + + count, err := m.BatchUpdate(keys, values, nil) + if err != nil { + t.Fatalf("BatchUpdate: %v", err) + } + if count != len(keys) { + t.Fatalf("BatchUpdate: expected count, %d, to be %d", count, len(keys)) + } + + var v uint32 + if err := m.Lookup(uint32(0), &v); err != nil { + t.Fatal("Can't lookup 0:", err) + } + if v != 42 { + t.Error("Want value 42, got", v) + } + + count, err = m.BatchDelete(keys, nil) + if err != nil { + t.Fatalf("BatchDelete: %v", err) + } + if count != len(keys) { + t.Fatalf("BatchDelete: expected %d deletions got %d", len(keys), count) + } + + if err := m.Lookup(uint32(0), &v); !errors.Is(err, ErrKeyNotExist) { + t.Fatalf("Lookup should have failed with error, %v, instead error is %v", ErrKeyNotExist, err) + } +} + func TestMapClose(t *testing.T) { m := createArray(t) diff --git a/marshalers.go b/marshalers.go index 3ea1021a8..25e28e1f5 100644 --- a/marshalers.go +++ b/marshalers.go @@ -141,7 +141,11 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er } alignedElemLength := align(elemLength, 8) - buf := make([]byte, alignedElemLength*possibleCPUs) + alignedSliceLen := possibleCPUs + if sliceLen > alignedSliceLen { + alignedSliceLen = sliceLen + } + buf := make([]byte, alignedElemLength*alignedSliceLen) for i := 0; i < sliceLen; i++ { elem := sliceValue.Index(i).Interface() diff --git a/syscalls.go b/syscalls.go index 714a78a94..271798b85 100644 --- a/syscalls.go +++ b/syscalls.go @@ -11,9 +11,7 @@ import ( ) // Generic errors returned by BPF syscalls. -var ( - ErrNotExist = errors.New("requested object does not exist") -) +var ErrNotExist = errors.New("requested object does not exist") // bpfObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. @@ -68,6 +66,17 @@ type bpfMapOpAttr struct { flags uint64 } +type bpfBatchMapOpAttr struct { + inBatch internal.Pointer + outBatch internal.Pointer + keys internal.Pointer + values internal.Pointer + count uint32 + mapFd uint32 + elemFlags uint64 + flags uint64 +} + type bpfMapInfo struct { map_type uint32 // since 4.12 1e2709769086 id uint32 @@ -321,6 +330,29 @@ func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) { return attr.nextID, wrapObjError(err) } +func bpfMapBatch(cmd internal.BPFCmd, m *internal.FD, inBatch, outBatch, keys, values internal.Pointer, count uint32, opts *BatchOptions) (uint32, error) { + fd, err := m.Value() + if err != nil { + return 0, err + } + + attr := bpfBatchMapOpAttr{ + inBatch: inBatch, + outBatch: outBatch, + keys: keys, + values: values, + count: count, + mapFd: fd, + } + if opts != nil { + attr.elemFlags = opts.ElemFlags + attr.flags = opts.Flags + } + _, err = internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + // always return count even on an error, as things like update might partially be fulfilled. + return attr.count, wrapMapError(err) +} + func wrapObjError(err error) error { if err == nil { return nil @@ -345,6 +377,10 @@ func wrapMapError(err error) error { return ErrKeyExist } + if errors.Is(err, unix.ENOTSUPP) { + return ErrNotSupported + } + return errors.New(err.Error()) } @@ -418,6 +454,32 @@ var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() return nil }) +var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error { + var maxEntries uint32 = 2 + attr := bpfMapCreateAttr{ + mapType: Hash, + keySize: 4, + valueSize: 4, + maxEntries: maxEntries, + } + + fd, err := bpfMapCreate(&attr) + if err != nil { + return internal.ErrNotSupported + } + defer fd.Close() + keys := []uint32{1, 2} + values := []uint32{3, 4} + kp, _ := marshalPtr(keys, 8) + vp, _ := marshalPtr(values, 8) + nilPtr := internal.NewPointer(nil) + _, err = bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, fd, nilPtr, nilPtr, kp, vp, maxEntries, nil) + if err != nil { + return internal.ErrNotSupported + } + return nil +}) + func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) { attr := bpfGetFDByIDAttr{ id: id, diff --git a/syscalls_test.go b/syscalls_test.go index 499d21570..4424719e1 100644 --- a/syscalls_test.go +++ b/syscalls_test.go @@ -33,6 +33,10 @@ func TestObjName(t *testing.T) { } } +func TestHaveBatchAPI(t *testing.T) { + testutils.CheckFeatureTest(t, haveBatchAPI) +} + func TestHaveObjName(t *testing.T) { testutils.CheckFeatureTest(t, haveObjName) } diff --git a/types.go b/types.go index 53e27ee8c..6927e2a00 100644 --- a/types.go +++ b/types.go @@ -201,3 +201,13 @@ const ( // Pin an object by using its name as the filename. PinByName ) + +// BatchOptions batch map operations options +// +// Mirrors libbpf struct bpf_map_batch_opts +// Currently BPF_F_FLAG is the only supported +// flag (for ElemFlags). +type BatchOptions struct { + ElemFlags uint64 + Flags uint64 +} From 847ab241b0f84680b8be3d5bb177279dfb480e6f Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Tue, 23 Feb 2021 10:38:26 +0000 Subject: [PATCH 2/4] map: remove mention of startKey from batch methods startKey is now called prevKey, remove mentions of the former. --- map.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/map.go b/map.go index 92cd0ce94..a0f16bca7 100644 --- a/map.go +++ b/map.go @@ -582,13 +582,12 @@ func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { return nil } -// BatchLookup looks up many elements in a map at once -// with the startKey being the first element to start -// from (nil if starting from the beginning is desired). +// BatchLookup looks up many elements in a map at once. +// // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "prevKey" is the key to start the batch lookup from, it will -// *not* be included in the results. +// *not* be included in the results. Use nil to start at the first key. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results @@ -597,14 +596,13 @@ func (m *Map) BatchLookup(prevKey, nextKeyOut, keysOut, valuesOut interface{}, o return m.batchLookup(internal.BPF_MAP_LOOKUP_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) } -// BatchLookupAndDelete looks up many elements in a map at once -// with the startKey being the first element to start -// from (nil if starting from the beginning is desired). +// BatchLookupAndDelete looks up many elements in a map at once, +// // It then deletes all those elements. // "keysOut" and "valuesOut" must be of type slice, a pointer // to a slice or buffer will not work. // "prevKey" is the key to start the batch lookup from, it will -// *not* be included in the results. +// *not* be included in the results. Use nil to start at the first key. // // ErrKeyNotExist is returned when the batch lookup has reached // the end of all possible results, even when partial results From 29d1a387097f703d632a93fe239b4e09d024f04a Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Tue, 23 Feb 2021 10:37:00 +0000 Subject: [PATCH 3/4] map: remove stray fmt.Println --- map.go | 1 - 1 file changed, 1 deletion(-) diff --git a/map.go b/map.go index a0f16bca7..e42a79092 100644 --- a/map.go +++ b/map.go @@ -649,7 +649,6 @@ func (m *Map) batchLookup(cmd internal.BPFCmd, startKey, nextKeyOut, keysOut, va nextPtr, nextBuf := makeBuffer(nextKeyOut, int(m.keySize)) ct, err := bpfMapBatch(cmd, m.fd, startPtr, nextPtr, keyPtr, valuePtr, uint32(count), opts) - fmt.Println(err) if err != nil { if !errors.Is(err, ErrKeyNotExist) { return 0, err From 95b312f24b5d4f518834edca77e8d0ff74dd45c8 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Tue, 23 Feb 2021 10:40:38 +0000 Subject: [PATCH 4/4] map: remove left over per-CPU marshaler changes --- map.go | 4 ++-- marshalers.go | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/map.go b/map.go index e42a79092..75abc7a1f 100644 --- a/map.go +++ b/map.go @@ -499,7 +499,7 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { return fmt.Errorf("can't marshal key: %w", err) } - valuePtr, err := m.marshalValue(value, false) + valuePtr, err := m.marshalValue(value) if err != nil { return fmt.Errorf("can't marshal value: %w", err) } @@ -897,7 +897,7 @@ func (m *Map) unmarshalKey(data interface{}, buf []byte) error { return unmarshalBytes(data, buf) } -func (m *Map) marshalValue(data interface{}, batch bool) (internal.Pointer, error) { +func (m *Map) marshalValue(data interface{}) (internal.Pointer, error) { if m.typ.hasPerCPUValue() { return marshalPerCPUValue(data, int(m.valueSize)) } diff --git a/marshalers.go b/marshalers.go index 25e28e1f5..3ea1021a8 100644 --- a/marshalers.go +++ b/marshalers.go @@ -141,11 +141,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er } alignedElemLength := align(elemLength, 8) - alignedSliceLen := possibleCPUs - if sliceLen > alignedSliceLen { - alignedSliceLen = sliceLen - } - buf := make([]byte, alignedElemLength*alignedSliceLen) + buf := make([]byte, alignedElemLength*possibleCPUs) for i := 0; i < sliceLen; i++ { elem := sliceValue.Index(i).Interface()