github.phpd.cn/cilium/cilium@v1.6.12/pkg/allocator/localkeys.go (about)

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