-
Notifications
You must be signed in to change notification settings - Fork 2
/
map_hash_of_maps.go
135 lines (110 loc) · 4.03 KB
/
map_hash_of_maps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package gobpfld
import (
"fmt"
"github.com/dylandreimerink/gobpfld/bpfsys"
"github.com/dylandreimerink/gobpfld/bpftypes"
"github.com/dylandreimerink/gobpfld/kernelsupport"
)
var _ BPFMap = (*HashOfMapsMap)(nil)
// HashOfMapsMap is a map with as value another map, the value type must be any loaded BPF map. The key type can be
// anything, the keys are hashed and thus do not need to be contiguous.
type HashOfMapsMap struct {
AbstractMap
// InnerMapDef is the definition of the inner map
// TODO: Once BTF is implemented we can infer this map type from the BTF debug symbols
InnerMapDef BPFMapDef
// innerMapDef is a copy of the publicly available map def, so it can't be change while the map is loaded
innerMapDef BPFMapDef
}
func (m *HashOfMapsMap) Load() error {
if m.Definition.Type != bpftypes.BPF_MAP_TYPE_HASH_OF_MAPS {
return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_HASH_OF_MAPS when using an HashOfMapsMap")
}
if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapHashOfMaps) {
return fmt.Errorf("Hash of maps map type is not supported by the current kernel version")
}
err := m.loadHashOfMaps()
if err != nil {
return err
}
err = mapRegister.add(m)
if err != nil {
return fmt.Errorf("map register: %w", err)
}
return nil
}
// loadPseudoInnerMap will load a map into the kernel with m.InnerMapDef as definition and returns the FD for this map.
func (m *HashOfMapsMap) loadPseudoInnerMap() (*AbstractMap, error) {
if kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapDynamicInnerMap) {
// Set the inner map flag
m.InnerMapDef.Flags |= bpftypes.BPFMapFlagsInnerMap
}
innerMap := &AbstractMap{
Name: MustNewObjName("pseudoinnermap"),
Definition: m.InnerMapDef,
}
return innerMap, innerMap.load(nil)
}
// loadArrayOfMaps will first create a pseudo map, the FD of which we need to pass when loading the outermap to provide
// type information. The verifier uses this type information to verify how values from the inner map are used.
func (m *HashOfMapsMap) loadHashOfMaps() error {
err := m.Definition.Validate()
if err != nil {
return err
}
innerMap, err := m.loadPseudoInnerMap()
if err != nil {
return fmt.Errorf("load pseudo inner map: %w", err)
}
// Copy the def so it can't be changed after loading
m.innerMapDef = m.InnerMapDef
err = m.load(func(attr *bpfsys.BPFAttrMapCreate) {
attr.InnerMapFD = innerMap.fd
})
if err != nil {
return fmt.Errorf("load: %w", err)
}
// After the outer map has been loaded, the inner map type info is copied so we can unload the pseudo inner map.
err = innerMap.close()
if err != nil {
return fmt.Errorf("inner pseudomap unload: %w", err)
}
return nil
}
// Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel
// if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map.
func (m *HashOfMapsMap) Close() error {
err := mapRegister.delete(m)
if err != nil {
return fmt.Errorf("map register: %w", err)
}
return m.close()
}
func (m *HashOfMapsMap) Get(key interface{}) (BPFMap, error) {
var id uint32
err := m.get(&key, &id)
if err != nil {
return nil, fmt.Errorf("map get: %w", err)
}
return MapFromID(id)
}
func (m *HashOfMapsMap) Set(key interface{}, value BPFMap, flags bpfsys.BPFAttrMapElemFlags) error {
def := value.GetDefinition()
if def.Flags&bpftypes.BPFMapFlagsInnerMap != 0 {
// If the inner map flag is set, max entries can be ignored when comparing inner maps.
// Since the Equal function doesn't take this edge case
// into account we will just make the MaxEntries of def equal.
// This doesn't update the actual value of the map since we are working with a copy of the definition
def.MaxEntries = m.innerMapDef.MaxEntries
}
if !def.Equal(m.innerMapDef) {
return fmt.Errorf("map definition of the 'value' doesn't match the inner map definition")
}
fd := value.GetFD()
return m.set(key, &fd, flags)
}
func (m *HashOfMapsMap) Iterator() MapIterator {
return &singleMapLookupIterator{
BPFMap: m,
}
}