Skip to content

Commit cc57200

Browse files
committed
Use GC rather than refcounting for VNID policy rules
1 parent 0f82878 commit cc57200

File tree

7 files changed

+231
-95
lines changed

7 files changed

+231
-95
lines changed

pkg/sdn/plugin/multitenant.go

Lines changed: 20 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ type multiTenantPlugin struct {
1515
node *OsdnNode
1616
vnids *nodeVNIDMap
1717

18-
vnidRefsLock sync.Mutex
19-
vnidRefs map[uint32]int
18+
vnidInUseLock sync.Mutex
19+
vnidInUse map[uint32]bool
2020
}
2121

2222
func NewMultiTenantPlugin() osdnPolicy {
2323
return &multiTenantPlugin{
24-
vnidRefs: make(map[uint32]int),
24+
vnidInUse: make(map[uint32]bool),
2525
}
2626
}
2727

@@ -66,14 +66,10 @@ func (mp *multiTenantPlugin) updatePodNetwork(namespace string, oldNetID, netID
6666
}
6767

6868
if oldNetID != netID {
69-
movedVNIDRefs := 0
70-
7169
// Update OF rules for the existing/old pods in the namespace
7270
for _, pod := range pods {
7371
err = mp.node.UpdatePod(pod)
74-
if err == nil {
75-
movedVNIDRefs++
76-
} else {
72+
if err != nil {
7773
glog.Errorf("Could not update pod %q in namespace %q: %v", pod.Name, namespace, err)
7874
}
7975
}
@@ -86,12 +82,10 @@ func (mp *multiTenantPlugin) updatePodNetwork(namespace string, oldNetID, netID
8682

8783
mp.node.DeleteServiceRules(&svc)
8884
mp.node.AddServiceRules(&svc, netID)
89-
movedVNIDRefs++
9085
}
9186

92-
if movedVNIDRefs > 0 {
93-
mp.moveVNIDRefs(movedVNIDRefs, oldNetID, netID)
94-
}
87+
mp.EnsureVNIDRules(netID)
88+
mp.RemoveVNIDRules(oldNetID)
9589

9690
// Update namespace references in egress firewall rules
9791
mp.node.UpdateEgressNetworkPolicyVNID(namespace, oldNetID, netID)
@@ -126,18 +120,19 @@ func (mp *multiTenantPlugin) GetMulticastEnabled(vnid uint32) bool {
126120
return mp.vnids.GetMulticastEnabled(vnid)
127121
}
128122

129-
func (mp *multiTenantPlugin) RefVNID(vnid uint32) {
123+
func (mp *multiTenantPlugin) EnsureVNIDRules(vnid uint32) {
130124
if vnid == 0 {
131125
return
132126
}
133127

134-
mp.vnidRefsLock.Lock()
135-
defer mp.vnidRefsLock.Unlock()
136-
mp.vnidRefs[vnid] += 1
137-
if mp.vnidRefs[vnid] > 1 {
128+
mp.vnidInUseLock.Lock()
129+
defer mp.vnidInUseLock.Unlock()
130+
if mp.vnidInUse[vnid] {
138131
return
139132
}
140-
glog.V(5).Infof("RefVNID %d adding rule", vnid)
133+
mp.vnidInUse[vnid] = true
134+
135+
glog.V(5).Infof("EnsureVNIDRules %d - adding rules", vnid)
141136

142137
otx := mp.node.oc.NewTransaction()
143138
otx.AddFlow("table=80, priority=100, reg0=%d, reg1=%d, actions=output:NXM_NX_REG2[]", vnid, vnid)
@@ -146,52 +141,20 @@ func (mp *multiTenantPlugin) RefVNID(vnid uint32) {
146141
}
147142
}
148143

149-
func (mp *multiTenantPlugin) UnrefVNID(vnid uint32) {
144+
func (mp *multiTenantPlugin) RemoveVNIDRules(vnid uint32) {
150145
if vnid == 0 {
151146
return
152147
}
153148

154-
mp.vnidRefsLock.Lock()
155-
defer mp.vnidRefsLock.Unlock()
156-
if mp.vnidRefs[vnid] == 0 {
157-
glog.Warningf("refcounting error on vnid %d", vnid)
158-
return
159-
}
160-
mp.vnidRefs[vnid] -= 1
161-
if mp.vnidRefs[vnid] > 0 {
162-
return
163-
}
164-
glog.V(5).Infof("UnrefVNID %d removing rule", vnid)
149+
mp.vnidInUseLock.Lock()
150+
defer mp.vnidInUseLock.Unlock()
151+
mp.vnidInUse[vnid] = false
152+
153+
glog.V(5).Infof("RemoveVNIDRules %d", vnid)
165154

166155
otx := mp.node.oc.NewTransaction()
167-
otx.DeleteFlows("table=80, reg0=%d, reg1=%d", vnid, vnid)
156+
otx.DeleteFlows("table=80, reg1=%d", vnid)
168157
if err := otx.EndTransaction(); err != nil {
169158
glog.Errorf("Error deleting OVS flow for VNID: %v", err)
170159
}
171160
}
172-
173-
func (mp *multiTenantPlugin) moveVNIDRefs(num int, oldVNID, newVNID uint32) {
174-
glog.V(5).Infof("moveVNIDRefs %d -> %d", oldVNID, newVNID)
175-
176-
mp.vnidRefsLock.Lock()
177-
defer mp.vnidRefsLock.Unlock()
178-
179-
otx := mp.node.oc.NewTransaction()
180-
if mp.vnidRefs[oldVNID] <= num {
181-
otx.DeleteFlows("table=80, reg0=%d, reg1=%d", oldVNID, oldVNID)
182-
}
183-
if mp.vnidRefs[newVNID] == 0 {
184-
otx.AddFlow("table=80, priority=100, reg0=%d, reg1=%d, actions=output:NXM_NX_REG2[]", newVNID, newVNID)
185-
}
186-
err := otx.EndTransaction()
187-
if err != nil {
188-
glog.Errorf("Error modifying OVS flows for VNID: %v", err)
189-
}
190-
191-
mp.vnidRefs[oldVNID] -= num
192-
if mp.vnidRefs[oldVNID] < 0 {
193-
glog.Warningf("refcounting error on vnid %d", oldVNID)
194-
mp.vnidRefs[oldVNID] = 0
195-
}
196-
mp.vnidRefs[newVNID] += num
197-
}

pkg/sdn/plugin/networkpolicy.go

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ type npNamespace struct {
4747
name string
4848
vnid uint32
4949
isolated bool
50-
refs int
5150
inUse bool
5251

5352
policies map[ktypes.UID]*npPolicy
@@ -121,7 +120,7 @@ func (np *networkPolicyPlugin) initNamespaces() error {
121120
name: ns.Name,
122121
vnid: vnid,
123122
isolated: namespaceIsIsolated(&ns),
124-
refs: 0,
123+
inUse: false,
125124
policies: make(map[ktypes.UID]*npPolicy),
126125
}
127126
}
@@ -164,7 +163,7 @@ func (np *networkPolicyPlugin) AddNetNamespace(netns *osapi.NetNamespace) {
164163
name: netns.NetName,
165164
vnid: netns.NetID,
166165
isolated: isolated,
167-
refs: 0,
166+
inUse: false,
168167
policies: make(map[ktypes.UID]*npPolicy),
169168
}
170169
}
@@ -197,15 +196,10 @@ func (np *networkPolicyPlugin) GetMulticastEnabled(vnid uint32) bool {
197196
}
198197

199198
func (np *networkPolicyPlugin) syncNamespace(npns *npNamespace) {
200-
inUse := npns.refs > 0
201-
if !inUse && !npns.inUse {
202-
return
203-
}
204-
205199
glog.V(5).Infof("syncNamespace %d", npns.vnid)
206200
otx := np.node.oc.NewTransaction()
207201
otx.DeleteFlows("table=80, reg1=%d", npns.vnid)
208-
if inUse {
202+
if npns.inUse {
209203
if npns.isolated {
210204
for _, npp := range npns.policies {
211205
for _, flow := range npp.flows {
@@ -219,36 +213,31 @@ func (np *networkPolicyPlugin) syncNamespace(npns *npNamespace) {
219213
if err := otx.EndTransaction(); err != nil {
220214
glog.Errorf("Error syncing OVS flows for VNID: %v", err)
221215
}
222-
npns.inUse = inUse
223216
}
224217

225-
func (np *networkPolicyPlugin) RefVNID(vnid uint32) {
218+
func (np *networkPolicyPlugin) EnsureVNIDRules(vnid uint32) {
226219
np.lock.Lock()
227220
defer np.lock.Unlock()
228221

229222
npns, exists := np.namespaces[vnid]
230-
if !exists {
223+
if !exists || npns.inUse {
231224
return
232225
}
233226

234-
npns.refs += 1
227+
npns.inUse = true
235228
np.syncNamespace(npns)
236229
}
237230

238-
func (np *networkPolicyPlugin) UnrefVNID(vnid uint32) {
231+
func (np *networkPolicyPlugin) RemoveVNIDRules(vnid uint32) {
239232
np.lock.Lock()
240233
defer np.lock.Unlock()
241234

242235
npns, exists := np.namespaces[vnid]
243236
if !exists {
244237
return
245238
}
246-
if npns.refs == 0 {
247-
glog.Warningf("refcounting error on vnid %d", vnid)
248-
return
249-
}
250239

251-
npns.refs -= 1
240+
npns.inUse = false
252241
np.syncNamespace(npns)
253242
}
254243

@@ -408,11 +397,15 @@ func (np *networkPolicyPlugin) watchNetworkPolicies() {
408397
switch delta.Type {
409398
case cache.Sync, cache.Added, cache.Updated:
410399
if changed := np.updateNetworkPolicy(npns, policy); changed {
411-
np.syncNamespace(npns)
400+
if npns.inUse {
401+
np.syncNamespace(npns)
402+
}
412403
}
413404
case cache.Deleted:
414405
delete(npns.policies, policy.UID)
415-
np.syncNamespace(npns)
406+
if npns.inUse {
407+
np.syncNamespace(npns)
408+
}
416409
}
417410

418411
return nil
@@ -522,7 +515,9 @@ func (np *networkPolicyPlugin) handleAddOrUpdateNamespace(obj, _ interface{}, ev
522515
np.kNamespaces[ns.Name] = *ns
523516
if npns, exists := np.namespaces[vnid]; exists {
524517
npns.isolated = namespaceIsIsolated(ns)
525-
np.syncNamespace(npns)
518+
if npns.inUse {
519+
np.syncNamespace(npns)
520+
}
526521
}
527522
// else the NetNamespace doesn't exist yet, but we will initialize
528523
// npns.isolated from the kapi.Namespace when it's created
@@ -555,7 +550,7 @@ func (np *networkPolicyPlugin) refreshNetworkPolicies(watchResourceName Resource
555550
}
556551
}
557552
}
558-
if changed {
553+
if changed && npns.inUse {
559554
np.syncNamespace(npns)
560555
}
561556
}

pkg/sdn/plugin/node.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ type osdnPolicy interface {
4545
GetNamespaces(vnid uint32) []string
4646
GetMulticastEnabled(vnid uint32) bool
4747

48-
RefVNID(vnid uint32)
49-
UnrefVNID(vnid uint32)
48+
EnsureVNIDRules(vnid uint32)
49+
RemoveVNIDRules(vnid uint32)
5050
}
5151

5252
type OsdnNode struct {
@@ -284,11 +284,13 @@ func (node *OsdnNode) Start() error {
284284
continue
285285
}
286286
if vnid, err := node.policy.GetVNID(p.Namespace); err == nil {
287-
node.policy.RefVNID(vnid)
287+
node.policy.EnsureVNIDRules(vnid)
288288
}
289289
}
290290
}
291291

292+
go kwait.Forever(node.syncVNIDRules, 24*time.Hour)
293+
292294
log.V(5).Infof("openshift-sdn network plugin ready")
293295
node.markPodNetworkReady()
294296

@@ -387,18 +389,17 @@ func (node *OsdnNode) handleAddOrUpdateService(obj, oldObj interface{}, eventTyp
387389
}
388390

389391
node.AddServiceRules(serv, netid)
390-
if !exists {
391-
node.policy.RefVNID(netid)
392-
}
392+
node.policy.EnsureVNIDRules(netid)
393393
}
394394

395395
func (node *OsdnNode) handleDeleteService(obj interface{}) {
396396
serv := obj.(*kapi.Service)
397397
log.V(5).Infof("Watch %s event for Service %q", watch.Deleted, serv.Name)
398398
node.DeleteServiceRules(serv)
399+
}
399400

400-
netid, err := node.policy.GetVNID(serv.Namespace)
401-
if err == nil {
402-
node.policy.UnrefVNID(netid)
401+
func (node *OsdnNode) syncVNIDRules() {
402+
for _, vnid := range node.oc.FindUnusedVNIDs() {
403+
node.policy.RemoveVNIDRules(uint32(vnid))
403404
}
404405
}

pkg/sdn/plugin/ovscontroller.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
osapi "github.com/openshift/origin/pkg/sdn/api"
1414
"github.com/openshift/origin/pkg/util/ovs"
1515

16+
"k8s.io/apimachinery/pkg/util/sets"
1617
kapi "k8s.io/kubernetes/pkg/api"
1718
)
1819

@@ -540,3 +541,64 @@ func (oc *ovsController) UpdateVXLANMulticastFlows(remoteIPs []string) error {
540541

541542
return otx.EndTransaction()
542543
}
544+
545+
// FindUnusedVNIDs returns a list of VNIDs for which there are table 80 "check" rules,
546+
// but no table 60/70 "load" rules (meaning that there are no longer any pods or services
547+
// on this node with that VNID). There is no locking with respect to other ovsController
548+
// actions, but as long the "add a pod" and "add a service" codepaths add the
549+
// pod/service-specific rules before they call policy.EnsureVNIDRules(), then there is no
550+
// race condition.
551+
func (oc *ovsController) FindUnusedVNIDs() []int {
552+
flows, err := oc.ovs.DumpFlows()
553+
if err != nil {
554+
glog.Errorf("FindUnusedVNIDs: could not DumpFlows: %v", err)
555+
return nil
556+
}
557+
558+
// inUseVNIDs is the set of VNIDs in use by pods or services on this node.
559+
// policyVNIDs is the set of VNIDs that we have rules for delivering to.
560+
inUseVNIDs := sets.NewInt()
561+
policyVNIDs := sets.NewInt()
562+
for _, flow := range flows {
563+
parsed, err := ovs.ParseFlow(ovs.ParseForDump, flow)
564+
if err != nil {
565+
glog.Warningf("FindUnusedVNIDs: could not parse flow %q: %v", flow, err)
566+
continue
567+
}
568+
569+
// A VNID is in use if there is a table 60 (services) or 70 (pods) flow that
570+
// loads that VNID into reg1 for later comparison.
571+
if parsed.Table == 60 || parsed.Table == 70 {
572+
// Can't use FindAction here since there may be multiple "load"s
573+
for _, action := range parsed.Actions {
574+
if action.Name != "load" || strings.Index(action.Value, "REG1") == -1 {
575+
continue
576+
}
577+
vnidEnd := strings.Index(action.Value, "->")
578+
if vnidEnd == -1 {
579+
continue
580+
}
581+
vnid, err := strconv.ParseInt(action.Value[:vnidEnd], 0, 32)
582+
if err != nil {
583+
glog.Warningf("FindUnusedVNIDs: could not parse VNID in 'load:%s': %v", action.Value, err)
584+
continue
585+
}
586+
inUseVNIDs.Insert(int(vnid))
587+
break
588+
}
589+
}
590+
591+
// A VNID is checked by policy if there is a table 80 rule comparing reg1 to it.
592+
if field, exists := parsed.FindField("reg1"); exists {
593+
vnid, err := strconv.ParseInt(field.Value, 0, 32)
594+
if err != nil {
595+
glog.Warningf("FindUnusedVNIDs: could not parse VNID in 'reg1=%s': %v", field.Value, err)
596+
continue
597+
}
598+
policyVNIDs.Insert(int(vnid))
599+
}
600+
601+
}
602+
603+
return policyVNIDs.Difference(inUseVNIDs).UnsortedList()
604+
}

0 commit comments

Comments
 (0)