github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/lbmap/bpfservice.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package lbmap 16 17 import ( 18 "fmt" 19 20 "github.com/cilium/cilium/pkg/loadbalancer" 21 ) 22 23 // serviceValueMap is a mapping from the Backend (IP:PORT) to its corresponding 24 // map value. 25 type serviceValueMap map[BackendAddrID]ServiceValue 26 27 type bpfBackend struct { 28 id BackendAddrID 29 isHole bool 30 bpfValue ServiceValue 31 } 32 33 type bpfService struct { 34 frontendKey ServiceKey 35 36 // backendsByMapIndex is the 1:1 representation of service backends as 37 // written into the BPF map. As service backends are scaled up or down, 38 // duplicate entries may be required to avoid moving backends to 39 // different map index slots. This map represents this and thus may 40 // contain duplicate backend entries in different map index slots. 41 backendsByMapIndex map[int]*bpfBackend 42 43 // uniqueBackends is a map of all service backends indexed by service 44 // backend ID. A backend may be listed multiple times in 45 // backendsByMapIndex, it will only be listed once in uniqueBackends. 46 uniqueBackends serviceValueMap 47 48 // backendsV2 is a map of all service v2 backends indexed by the legacy IDs. 49 // A backend can only be listed once in the map. 50 // TODO(brb) use list instead to preserve the ordering when svc backends change. 51 backendsV2 serviceValueMap 52 } 53 54 func newBpfService(key ServiceKey) *bpfService { 55 return &bpfService{ 56 frontendKey: key, 57 backendsByMapIndex: map[int]*bpfBackend{}, 58 uniqueBackends: serviceValueMap{}, 59 backendsV2: serviceValueMap{}, 60 } 61 } 62 63 // getBackendsV2 makes a copy of backendsV2, so that they are safe to use 64 // after the bpfService lock has been released. 65 func (b *bpfService) getBackendsV2() serviceValueMap { 66 backends := make(serviceValueMap, len(b.backendsV2)) 67 for addrID, backend := range b.backendsV2 { 68 backends[addrID] = backend 69 } 70 71 return backends 72 } 73 74 type lbmapCache struct { 75 entries map[string]*bpfService 76 backendRefCount map[BackendAddrID]int 77 backendIDByAddrID map[BackendAddrID]BackendKey 78 } 79 80 func newLBMapCache() lbmapCache { 81 return lbmapCache{ 82 entries: map[string]*bpfService{}, 83 backendRefCount: map[BackendAddrID]int{}, 84 backendIDByAddrID: map[BackendAddrID]BackendKey{}, 85 } 86 } 87 88 func createBackendsMap(backends []ServiceValue) serviceValueMap { 89 m := serviceValueMap{} 90 for _, b := range backends { 91 m[b.BackendAddrID()] = b 92 } 93 return m 94 } 95 96 // restoreService restores service cache of the given legacy and v2 service. 97 func (l *lbmapCache) restoreService(svc loadbalancer.LBSVC) error { 98 serviceKey, serviceValues, err := LBSVC2ServiceKeynValue(svc) 99 if err != nil { 100 return err 101 } 102 103 frontendID := serviceKey.String() 104 105 bpfSvc, ok := l.entries[frontendID] 106 if !ok { 107 bpfSvc = newBpfService(serviceKey) 108 l.entries[frontendID] = bpfSvc 109 } 110 111 for _, backend := range serviceValues { 112 addrID := backend.BackendAddrID() 113 if _, found := bpfSvc.backendsV2[addrID]; !found { 114 l.addBackendV2Locked(addrID) 115 } 116 bpfSvc.backendsV2[addrID] = backend 117 } 118 119 return nil 120 } 121 122 // prepareUpdate prepares the caches to reflect the changes in the given svc. 123 // The given backends should not contain a service value of a master service. 124 func (l *lbmapCache) prepareUpdate(fe ServiceKey, backends []ServiceValue) ( 125 *bpfService, map[loadbalancer.BackendID]ServiceValue, []BackendKey, error) { 126 127 frontendID := fe.String() 128 129 bpfSvc, ok := l.entries[frontendID] 130 if !ok { 131 bpfSvc = newBpfService(fe) 132 l.entries[frontendID] = bpfSvc 133 } 134 135 newBackendsMap := createBackendsMap(backends) 136 toRemoveBackendIDs := []BackendKey{} 137 toAddBackendIDs := map[loadbalancer.BackendID]ServiceValue{} 138 139 // Step 1: Delete all backends that no longer exist. 140 for addrID := range bpfSvc.backendsV2 { 141 if _, ok := newBackendsMap[addrID]; !ok { 142 isLastInstanceRemoved, err := l.delBackendV2Locked(addrID) 143 if err != nil { 144 return nil, nil, nil, err 145 } 146 if isLastInstanceRemoved { 147 toRemoveBackendIDs = append(toRemoveBackendIDs, 148 l.backendIDByAddrID[addrID]) 149 delete(l.backendIDByAddrID, addrID) 150 } 151 delete(bpfSvc.backendsV2, addrID) 152 } 153 } 154 155 // Step 2: Add all backends that don't exist in the service yet. 156 for _, b := range backends { 157 addrID := b.BackendAddrID() 158 if _, ok := bpfSvc.backendsV2[addrID]; !ok { 159 bpfSvc.backendsV2[addrID] = b 160 isNew := l.addBackendV2Locked(addrID) 161 if isNew { 162 toAddBackendIDs[l.backendIDByAddrID[addrID].GetID()] = b 163 } 164 } 165 } 166 167 return bpfSvc, toAddBackendIDs, toRemoveBackendIDs, nil 168 } 169 170 func (l *lbmapCache) delete(fe ServiceKey) { 171 delete(l.entries, fe.String()) 172 } 173 174 // addBackendV2Locked increments a ref count for the given backend and returns 175 // whether any instance of the backend existed before. 176 func (l *lbmapCache) addBackendV2Locked(addrID BackendAddrID) bool { 177 l.backendRefCount[addrID]++ 178 return l.backendRefCount[addrID] == 1 179 } 180 181 // delBackendV2Locked decrements a ref count for the given backend aand returns 182 // whether there are any instance of the backend left. 183 func (l *lbmapCache) delBackendV2Locked(addrID BackendAddrID) (bool, error) { 184 count, found := l.backendRefCount[addrID] 185 if !found { 186 return false, fmt.Errorf("backend %s not found", addrID) 187 } 188 189 if count == 1 { 190 delete(l.backendRefCount, addrID) 191 return true, nil 192 } 193 194 l.backendRefCount[addrID]-- 195 return false, nil 196 } 197 198 func (l *lbmapCache) addBackendIDs(backendIDs map[BackendAddrID]BackendKey) { 199 for addrID, backendID := range backendIDs { 200 l.backendIDByAddrID[addrID] = backendID 201 } 202 } 203 204 // filterNewBackends filters out backends which already exists from the given 205 // map (i.e. keeps only new backends). 206 func (l *lbmapCache) filterNewBackends(backends serviceValueMap) serviceValueMap { 207 newBackends := serviceValueMap{} 208 209 for addrID, b := range backends { 210 if _, found := l.backendIDByAddrID[addrID]; !found { 211 newBackends[addrID] = b 212 } 213 } 214 215 return newBackends 216 } 217 218 func (l *lbmapCache) getBackendKey(addrID BackendAddrID) BackendKey { 219 return l.backendIDByAddrID[addrID] 220 } 221 222 // removeServiceV2 removes the service v2 from the cache. 223 func (l *lbmapCache) removeServiceV2(svcKey ServiceKeyV2) ([]BackendKey, int, error) { 224 frontendID := svcKey.String() 225 bpfSvc, ok := l.entries[frontendID] 226 if !ok { 227 return nil, 0, fmt.Errorf("Service %s not found", frontendID) 228 } 229 230 backendsToRemove := []BackendKey{} 231 count := len(bpfSvc.backendsV2) 232 233 for addrID := range bpfSvc.backendsV2 { 234 isLastInstance, err := l.delBackendV2Locked(addrID) 235 if err != nil { 236 return nil, 0, err 237 } 238 if isLastInstance { 239 backendsToRemove = append(backendsToRemove, l.backendIDByAddrID[addrID]) 240 delete(l.backendIDByAddrID, addrID) 241 } 242 } 243 244 // FIXME(brb) uncomment the following line after we have removed the support for 245 // legacy svc. 246 //delete(l.entries, frontendID) 247 248 return backendsToRemove, count, nil 249 } 250 251 // removeBackendsWithRefCountZero removes backends from the cache which are not 252 // used by any service. 253 func (l *lbmapCache) removeBackendsWithRefCountZero() map[BackendAddrID]BackendKey { 254 removed := make(map[BackendAddrID]BackendKey) 255 256 for addrID, id := range l.backendIDByAddrID { 257 if l.backendRefCount[addrID] == 0 { 258 delete(l.backendIDByAddrID, addrID) 259 removed[addrID] = id 260 } 261 } 262 263 return removed 264 }