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 }