github.com/cilium/cilium@v1.16.2/pkg/ebpf/map.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ebpf 5 6 import ( 7 "errors" 8 "fmt" 9 "os" 10 "path/filepath" 11 12 ciliumebpf "github.com/cilium/ebpf" 13 14 "github.com/cilium/cilium/api/v1/models" 15 "github.com/cilium/cilium/pkg/bpf" 16 "github.com/cilium/cilium/pkg/lock" 17 "github.com/cilium/cilium/pkg/metrics" 18 ) 19 20 type MapSpec = ciliumebpf.MapSpec 21 22 type PinType = ciliumebpf.PinType 23 24 const ( 25 Hash = ciliumebpf.Hash 26 PerCPUHash = ciliumebpf.PerCPUHash 27 Array = ciliumebpf.Array 28 HashOfMaps = ciliumebpf.HashOfMaps 29 LPMTrie = ciliumebpf.LPMTrie 30 LRUHash = ciliumebpf.LRUHash 31 32 PinNone = ciliumebpf.PinNone 33 PinByName = ciliumebpf.PinByName 34 ) 35 36 var ( 37 ErrKeyNotExist = ciliumebpf.ErrKeyNotExist 38 ) 39 40 // IterateCallback represents the signature of the callback function expected by 41 // the IterateWithCallback method, which in turn is used to iterate all the 42 // keys/values of a map. 43 type IterateCallback func(key, value interface{}) 44 45 // Map represents an eBPF map. 46 type Map struct { 47 lock lock.RWMutex 48 *ciliumebpf.Map 49 50 spec *MapSpec 51 path string 52 } 53 54 // NewMap creates a new Map object. 55 func NewMap(spec *MapSpec) *Map { 56 return &Map{ 57 spec: spec, 58 } 59 } 60 61 // LoadRegisterMap loads the specified map from a bpffs pin path and registers 62 // its handle in the package-global map register. 63 func LoadRegisterMap(mapName string) (*Map, error) { 64 path := bpf.MapPath(mapName) 65 66 m, err := LoadPinnedMap(path) 67 if err != nil { 68 return nil, err 69 } 70 71 registerMap(m) 72 73 return m, nil 74 } 75 76 // LoadPinnedMap wraps cilium/ebpf's LoadPinnedMap. 77 func LoadPinnedMap(fileName string) (*Map, error) { 78 m, err := ciliumebpf.LoadPinnedMap(fileName, nil) 79 if err != nil { 80 return nil, err 81 } 82 83 return &Map{ 84 Map: m, 85 path: fileName, 86 }, nil 87 } 88 89 func MapFromID(id int) (*Map, error) { 90 newMap, err := ciliumebpf.NewMapFromID(ciliumebpf.MapID(id)) 91 if err != nil { 92 return nil, err 93 } 94 95 return &Map{ 96 Map: newMap, 97 }, nil 98 } 99 100 // OpenOrCreate tries to open or create the eBPF map identified by the spec in 101 // the Map object. 102 func (m *Map) OpenOrCreate() error { 103 m.lock.Lock() 104 defer m.lock.Unlock() 105 106 if m.Map != nil { 107 return nil 108 } 109 110 if m.spec == nil { 111 return fmt.Errorf("cannot create map: nil map spec") 112 } 113 114 opts := ciliumebpf.MapOptions{ 115 PinPath: bpf.TCGlobalsPath(), 116 } 117 118 m.spec.Flags |= bpf.GetPreAllocateMapFlags(m.spec.Type) 119 120 path := bpf.MapPath(m.spec.Name) 121 122 if m.spec.Pinning == ciliumebpf.PinByName { 123 mapDir := filepath.Dir(path) 124 125 if _, err := os.Stat(mapDir); os.IsNotExist(err) { 126 if err = os.MkdirAll(mapDir, 0755); err != nil { 127 return &os.PathError{ 128 Op: "Unable create map base directory", 129 Path: path, 130 Err: err, 131 } 132 } 133 } 134 } 135 136 newMap, err := ciliumebpf.NewMapWithOptions(m.spec, opts) 137 if err != nil { 138 if !errors.Is(err, ciliumebpf.ErrMapIncompatible) { 139 return fmt.Errorf("unable to create map: %w", err) 140 } 141 142 // There already exists a pinned map but it has a different 143 // configuration (e.g different type, k/v size or flags). 144 // Try to delete and recreate it. 145 146 log.WithField("map", m.spec.Name). 147 WithError(err).Warn("Removing map to allow for property upgrade (expect map data loss)") 148 149 oldMap, err := ciliumebpf.LoadPinnedMap(path, &opts.LoadPinOptions) 150 if err != nil { 151 return fmt.Errorf("cannot load pinned map %s: %w", m.spec.Name, err) 152 } 153 defer func() { 154 if err := oldMap.Close(); err != nil { 155 log.WithField("map", m.spec.Name).Warnf("Cannot close map: %v", err) 156 } 157 }() 158 159 if err = oldMap.Unpin(); err != nil { 160 return fmt.Errorf("cannot unpin map %s: %w", m.spec.Name, err) 161 } 162 163 newMap, err = ciliumebpf.NewMapWithOptions(m.spec, opts) 164 if err != nil { 165 return fmt.Errorf("unable to create map: %w", err) 166 } 167 } 168 169 m.Map = newMap 170 m.path = path 171 172 registerMap(m) 173 metrics.UpdateMapCapacity(m.spec.Name, m.spec.MaxEntries) 174 return nil 175 } 176 177 // IterateWithCallback iterates through all the keys/values of a map, passing 178 // each key/value pair to the cb callback. 179 func (m *Map) IterateWithCallback(key, value interface{}, cb IterateCallback) error { 180 if m.Map == nil { 181 if err := m.OpenOrCreate(); err != nil { 182 return err 183 } 184 } 185 186 m.lock.RLock() 187 defer m.lock.RUnlock() 188 189 entries := m.Iterate() 190 for entries.Next(key, value) { 191 cb(key, value) 192 } 193 194 return nil 195 } 196 197 // GetModel returns a BPF map in the representation served via the API. 198 func (m *Map) GetModel() *models.BPFMap { 199 m.lock.RLock() 200 defer m.lock.RUnlock() 201 202 mapModel := &models.BPFMap{ 203 Path: m.path, 204 } 205 206 // TODO: handle map cache. See pkg/bpf/map_linux.go:GetModel() 207 208 return mapModel 209 }