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  }