github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/emulator/maps_hash_lru.go (about) 1 package emulator 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 8 "github.com/dylandreimerink/gobpfld/bpfsys" 9 ) 10 11 type HashMapLRU struct { 12 AbstractMap 13 14 // Go can't use slices as map values, so what we do is we sha256 hash the slice which always results in a 15 // uniform sized array which we can use as key. Since we now don't index by the actual key, we also need to 16 // store the actual key value so we can return k/v pairs 17 18 KeysMap map[hashKey]*ByteMemory 19 Values map[hashKey]*ByteMemory 20 21 // Keep track of recent usage. Every time a key is updated or looked up, it is moved to the top of the slice. 22 // The bottom most key is thus always the least recently used and will be overwritten if an entry is added to 23 // the map while full. 24 UsageList []hashKey 25 } 26 27 type hashKey [sha256.Size]byte 28 29 func (m *HashMapLRU) Init() error { 30 m.KeysMap = make(map[hashKey]*ByteMemory) 31 m.Values = make(map[hashKey]*ByteMemory) 32 33 // NOTE: we always ignore the BPFMapFlagsNoPreAlloc flag since we never pre-alloc, it is an optimization which 34 // we don't need for the current purposes of the emulator. 35 36 return nil 37 } 38 39 func (m *HashMapLRU) Keys() []RegisterValue { 40 keys := make([]RegisterValue, len(m.KeysMap)) 41 i := 0 42 for _, val := range m.KeysMap { 43 keys[i] = &MemoryPtr{ 44 Memory: val.Clone(), // Clone since we don't want to give access to a modifyable reference 45 } 46 i++ 47 } 48 return keys 49 } 50 51 func (m *HashMapLRU) promote(key hashKey) { 52 cur := 0 53 for i, k := range m.UsageList { 54 if k == key { 55 cur = i 56 break 57 } 58 } 59 // Key not found, or already at the top, nothing to do 60 if cur == 0 { 61 return 62 } 63 64 // Move all keys above cur 1 index down 65 copy(m.UsageList[1:cur+1], m.UsageList[:cur]) 66 // Set cur a index 0 67 m.UsageList[0] = key 68 } 69 70 func (m *HashMapLRU) Lookup(key RegisterValue) (RegisterValue, error) { 71 keyPtr, ok := key.(PointerValue) 72 if !ok { 73 return nil, errMapKeyNoPtr 74 } 75 76 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 77 if !ok { 78 return nil, fmt.Errorf("key read range: %w", err) 79 } 80 81 keyHash := sha256.Sum256(keyVal) 82 value, found := m.Values[keyHash] 83 if !found { 84 // Return NULL if value doesn't exist 85 return newIMM(0), nil 86 } 87 88 m.promote(keyHash) 89 90 return &MemoryPtr{Memory: value, Offset: 0}, nil 91 } 92 93 func (m *HashMapLRU) Update( 94 key RegisterValue, 95 value RegisterValue, 96 flags bpfsys.BPFAttrMapElemFlags, 97 ) ( 98 RegisterValue, 99 error, 100 ) { 101 keyPtr, ok := key.(PointerValue) 102 if !ok { 103 return nil, errMapKeyNoPtr 104 } 105 106 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 107 if !ok { 108 return nil, fmt.Errorf("key read range: %w", err) 109 } 110 111 keyHash := sha256.Sum256(keyVal) 112 existingValue, found := m.Values[keyHash] 113 if !found { 114 // If map is full 115 if len(m.Values)+1 > int(m.Def.MaxEntries) { 116 // Find the least recently used key, and delete it, 117 lru := m.UsageList[len(m.UsageList)-1] 118 m.delete(lru) 119 } 120 121 existingValue = &ByteMemory{ 122 MemName: fmt.Sprintf("%s[0x%s]", m.Name, hex.EncodeToString(keyVal)), 123 Backing: make([]byte, m.Def.ValueSize), 124 } 125 } 126 existingKey, found := m.KeysMap[keyHash] 127 if !found { 128 existingKey = &ByteMemory{ 129 MemName: hex.EncodeToString(keyVal), 130 Backing: make([]byte, m.Def.KeySize), 131 } 132 } 133 134 valPtr, ok := value.(PointerValue) 135 if !ok { 136 return nil, errMapValNoPtr 137 } 138 139 valVal, err := valPtr.ReadRange(0, int(m.Def.ValueSize)) 140 if !ok { 141 return nil, fmt.Errorf("val read range: %w", err) 142 } 143 144 // If the key is new, add it to the usage list 145 if !found { 146 // Add new key to the usage list 147 m.UsageList = append(m.UsageList, keyHash) 148 } 149 // Promote the key, since it is now the most recently used 150 m.promote(keyHash) 151 152 // We can safely assing, valVal is otherwise unreferenced 153 existingValue.Backing = valVal 154 m.Values[keyHash] = existingValue 155 156 // We can safely assing, valVal is otherwise unreferenced 157 existingKey.Backing = keyVal 158 m.KeysMap[keyHash] = existingKey 159 160 return newIMM(0), nil 161 } 162 163 func (m *HashMapLRU) delete(key hashKey) { 164 cur := -1 165 for i, k := range m.UsageList { 166 if k == key { 167 cur = i 168 break 169 } 170 } 171 // Key not found, nothing to do 172 if cur == -1 { 173 return 174 } 175 176 // Move all keys below cur up by 1 spot 177 copy(m.UsageList[cur:], m.UsageList[cur+1:]) 178 // Shrink the slice 179 m.UsageList = m.UsageList[:len(m.UsageList)-1] 180 181 delete(m.KeysMap, key) 182 delete(m.Values, key) 183 } 184 185 func (m *HashMapLRU) Delete(key RegisterValue, flags bpfsys.BPFAttrMapElemFlags) error { 186 keyPtr, ok := key.(PointerValue) 187 if !ok { 188 return errMapKeyNoPtr 189 } 190 191 keyVal, err := keyPtr.ReadRange(0, int(m.Def.KeySize)) 192 if !ok { 193 return fmt.Errorf("key read range: %w", err) 194 } 195 keyHash := sha256.Sum256(keyVal) 196 197 m.delete(keyHash) 198 199 return nil 200 }