github.com/cilium/cilium@v1.16.2/pkg/allocator/localkeys.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package allocator
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/sirupsen/logrus"
    10  
    11  	"github.com/cilium/cilium/pkg/idpool"
    12  	"github.com/cilium/cilium/pkg/kvstore"
    13  	"github.com/cilium/cilium/pkg/lock"
    14  )
    15  
    16  type localKey struct {
    17  	val    idpool.ID
    18  	key    AllocatorKey
    19  	refcnt uint64
    20  
    21  	// verified is true when the key has been synced with the kvstore
    22  	verified bool
    23  }
    24  
    25  // localKeys is a map of keys in use locally. Keys can be used multiple times.
    26  // A refcnt is managed to know when a key is no longer in use
    27  type localKeys struct {
    28  	lock.RWMutex
    29  	keys map[string]*localKey
    30  	ids  map[idpool.ID]*localKey
    31  }
    32  
    33  func newLocalKeys() *localKeys {
    34  	return &localKeys{
    35  		keys: map[string]*localKey{},
    36  		ids:  map[idpool.ID]*localKey{},
    37  	}
    38  }
    39  
    40  // allocate creates an entry for key in localKeys if needed and increments the
    41  // refcnt. The value associated with the key must match the local cache or an
    42  // error is returned
    43  func (lk *localKeys) allocate(keyString string, key AllocatorKey, val idpool.ID) (idpool.ID, bool, error) {
    44  	lk.Lock()
    45  	defer lk.Unlock()
    46  
    47  	var firstUse bool
    48  
    49  	if k, ok := lk.keys[keyString]; ok {
    50  		if val != k.val {
    51  			return idpool.NoID, firstUse, fmt.Errorf("local key already allocated with different value (%s != %s)", val, k.val)
    52  		}
    53  
    54  		k.refcnt++
    55  		kvstore.Trace("Incremented local key refcnt", nil, logrus.Fields{fieldKey: keyString, fieldID: val, fieldRefCnt: k.refcnt})
    56  		return k.val, firstUse, nil
    57  	}
    58  
    59  	firstUse = true
    60  	k := &localKey{key: key, val: val, refcnt: 1}
    61  	lk.keys[keyString] = k
    62  	lk.ids[val] = k
    63  	kvstore.Trace("New local key", nil, logrus.Fields{fieldKey: keyString, fieldID: val, fieldRefCnt: 1})
    64  	return val, firstUse, nil
    65  }
    66  
    67  func (lk *localKeys) verify(key string) error {
    68  	lk.Lock()
    69  	defer lk.Unlock()
    70  
    71  	if k, ok := lk.keys[key]; ok {
    72  		k.verified = true
    73  		kvstore.Trace("Local key verified", nil, logrus.Fields{fieldKey: key})
    74  		return nil
    75  	}
    76  
    77  	return fmt.Errorf("key %s not found", key)
    78  }
    79  
    80  // lookupKey returns the idpool.ID of the key is present in the map of keys.
    81  // if it isn't present, returns idpool.NoID
    82  func (lk *localKeys) lookupKey(key string) idpool.ID {
    83  	lk.RLock()
    84  	defer lk.RUnlock()
    85  
    86  	if k, ok := lk.keys[key]; ok {
    87  		return k.val
    88  	}
    89  
    90  	return idpool.NoID
    91  }
    92  
    93  // lookupID returns the key for a given ID or an empty string
    94  func (lk *localKeys) lookupID(id idpool.ID) AllocatorKey {
    95  	lk.RLock()
    96  	defer lk.RUnlock()
    97  
    98  	if k, ok := lk.ids[id]; ok {
    99  		return k.key
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  // use increments the refcnt of the key and returns its value
   106  func (lk *localKeys) use(key string) idpool.ID {
   107  	lk.Lock()
   108  	defer lk.Unlock()
   109  
   110  	if k, ok := lk.keys[key]; ok {
   111  		// unverified keys behave as if they do not exist
   112  		if !k.verified {
   113  			return idpool.NoID
   114  		}
   115  
   116  		k.refcnt++
   117  		kvstore.Trace("Incremented local key refcnt", nil, logrus.Fields{fieldKey: key, fieldID: k.val, fieldRefCnt: k.refcnt})
   118  		return k.val
   119  	}
   120  
   121  	return idpool.NoID
   122  }
   123  
   124  // release releases the refcnt of a key. It returns the ID associated with the
   125  // given key. When the last reference was released, the key is deleted and the
   126  // returned lastUse value is true.
   127  func (lk *localKeys) release(key string) (lastUse bool, id idpool.ID, err error) {
   128  	lk.Lock()
   129  	defer lk.Unlock()
   130  	if k, ok := lk.keys[key]; ok {
   131  		k.refcnt--
   132  		kvstore.Trace("Decremented local key refcnt", nil, logrus.Fields{fieldKey: key, fieldID: k.val, fieldRefCnt: k.refcnt})
   133  		if k.refcnt == 0 {
   134  			delete(lk.keys, key)
   135  			delete(lk.ids, k.val)
   136  			return true, k.val, nil
   137  		}
   138  
   139  		return false, k.val, nil
   140  	}
   141  
   142  	return false, idpool.NoID, fmt.Errorf("unable to find key in local cache")
   143  }
   144  
   145  func (lk *localKeys) getVerifiedIDs() map[idpool.ID]AllocatorKey {
   146  	ids := map[idpool.ID]AllocatorKey{}
   147  	lk.RLock()
   148  	for id, localKey := range lk.ids {
   149  		if localKey.verified {
   150  			ids[id] = localKey.key
   151  		}
   152  	}
   153  	lk.RUnlock()
   154  
   155  	return ids
   156  }