Carmelo Cascone
Committed by Jonathan Hart

Improved consistency for BMv2 flow rules handling

Change-Id: I3a4798af3f35f135e8162385a1bf7fc059028307
...@@ -24,6 +24,7 @@ import org.onosproject.bmv2.api.context.Bmv2DeviceContext; ...@@ -24,6 +24,7 @@ import org.onosproject.bmv2.api.context.Bmv2DeviceContext;
24 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator; 24 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
25 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException; 25 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslatorException;
26 import org.onosproject.bmv2.api.context.Bmv2Interpreter; 26 import org.onosproject.bmv2.api.context.Bmv2Interpreter;
27 +import org.onosproject.bmv2.api.context.Bmv2TableModel;
27 import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent; 28 import org.onosproject.bmv2.api.runtime.Bmv2DeviceAgent;
28 import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper; 29 import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
29 import org.onosproject.bmv2.api.runtime.Bmv2MatchKey; 30 import org.onosproject.bmv2.api.runtime.Bmv2MatchKey;
...@@ -112,10 +113,10 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -112,10 +113,10 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
112 113
113 List<FlowEntry> entryList = Lists.newArrayList(); 114 List<FlowEntry> entryList = Lists.newArrayList();
114 115
115 - configuration.tables().forEach(table -> { 116 + for (Bmv2TableModel table : configuration.tables()) {
116 // For each table in the configuration AND exposed by the interpreter. 117 // For each table in the configuration AND exposed by the interpreter.
117 if (!interpreter.tableIdMap().inverse().containsKey(table.name())) { 118 if (!interpreter.tableIdMap().inverse().containsKey(table.name())) {
118 - return; 119 + continue; // next table
119 } 120 }
120 121
121 List<Bmv2ParsedTableEntry> installedEntries; 122 List<Bmv2ParsedTableEntry> installedEntries;
...@@ -123,33 +124,34 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -123,33 +124,34 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
123 installedEntries = deviceAgent.getTableEntries(table.name()); 124 installedEntries = deviceAgent.getTableEntries(table.name());
124 } catch (Bmv2RuntimeException e) { 125 } catch (Bmv2RuntimeException e) {
125 log.warn("Failed to get table entries of table {} of {}: {}", table.name(), deviceId, e.explain()); 126 log.warn("Failed to get table entries of table {} of {}: {}", table.name(), deviceId, e.explain());
126 - return; 127 + continue; // next table
127 } 128 }
128 129
129 - installedEntries.forEach(parsedEntry -> { 130 + for (Bmv2ParsedTableEntry parsedEntry : installedEntries) {
130 - Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, 131 + Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, table.name(),
131 - table.name(),
132 parsedEntry.matchKey()); 132 parsedEntry.matchKey());
133 - ENTRY_LOCKS.compute(entryRef, (key, value) -> {
134 133
135 - Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef); 134 + ENTRY_LOCKS.putIfAbsent(entryRef, true);
135 + synchronized (ENTRY_LOCKS.get(entryRef)) {
136 +
137 + Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
136 138
137 if (frWrapper == null) { 139 if (frWrapper == null) {
138 log.warn("missing reference from table entry service, BUG? " + 140 log.warn("missing reference from table entry service, BUG? " +
139 "deviceId={}, tableName={}, matchKey={}", 141 "deviceId={}, tableName={}, matchKey={}",
140 deviceId, table.name(), entryRef.matchKey()); 142 deviceId, table.name(), entryRef.matchKey());
141 - return null; 143 + continue; // next entry
142 } 144 }
143 145
144 long remoteEntryId = parsedEntry.entryId(); 146 long remoteEntryId = parsedEntry.entryId();
145 long localEntryId = frWrapper.entryId(); 147 long localEntryId = frWrapper.entryId();
146 148
147 if (remoteEntryId != localEntryId) { 149 if (remoteEntryId != localEntryId) {
148 - log.warn("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}", 150 + log.debug("getFlowEntries(): inconsistent entry id! BUG? Updating it... remote={}, local={}",
149 remoteEntryId, localEntryId); 151 remoteEntryId, localEntryId);
150 frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId, 152 frWrapper = new Bmv2FlowRuleWrapper(frWrapper.rule(), remoteEntryId,
151 frWrapper.creationDate()); 153 frWrapper.creationDate());
152 - tableEntryService.bindEntryReference(entryRef, frWrapper); 154 + tableEntryService.bind(entryRef, frWrapper);
153 } 155 }
154 156
155 long bytes = 0L; 157 long bytes = 0L;
...@@ -171,11 +173,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -171,11 +173,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
171 FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(), 173 FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
172 packets, bytes); 174 packets, bytes);
173 entryList.add(entry); 175 entryList.add(entry);
174 - return true; 176 + }
175 - }); 177 + }
176 - 178 + }
177 - });
178 - });
179 179
180 return Collections.unmodifiableCollection(entryList); 180 return Collections.unmodifiableCollection(entryList);
181 } 181 }
...@@ -226,19 +226,16 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -226,19 +226,16 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
226 bmv2Entry = translator.translate(rule, context); 226 bmv2Entry = translator.translate(rule, context);
227 } catch (Bmv2FlowRuleTranslatorException e) { 227 } catch (Bmv2FlowRuleTranslatorException e) {
228 log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule); 228 log.warn("Unable to translate flow rule: {} - {}", e.getMessage(), rule);
229 - continue; 229 + continue; // next rule
230 } 230 }
231 231
232 String tableName = bmv2Entry.tableName(); 232 String tableName = bmv2Entry.tableName();
233 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey()); 233 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
234 234
235 - /* 235 + ENTRY_LOCKS.putIfAbsent(entryRef, true);
236 - From here on threads are synchronized over entryKey, i.e. serialize operations 236 + synchronized (ENTRY_LOCKS.get(entryRef)) {
237 - over the same matchKey of a specific table and device.
238 - */
239 - ENTRY_LOCKS.compute(entryRef, (key, value) -> {
240 // Get from store 237 // Get from store
241 - Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookupEntryReference(entryRef); 238 + Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
242 try { 239 try {
243 if (operation == Operation.APPLY) { 240 if (operation == Operation.APPLY) {
244 // Apply entry 241 // Apply entry
...@@ -269,15 +266,14 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -269,15 +266,14 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
269 } catch (Bmv2RuntimeException e) { 266 } catch (Bmv2RuntimeException e) {
270 log.warn("Unable to {} flow rule: {}", operation.name(), e.explain()); 267 log.warn("Unable to {} flow rule: {}", operation.name(), e.explain());
271 } 268 }
272 - // Update binding in table entry service. 269 +
270 + // Update entryRef binding in table entry service.
273 if (frWrapper != null) { 271 if (frWrapper != null) {
274 - tableEntryService.bindEntryReference(entryRef, frWrapper); 272 + tableEntryService.bind(entryRef, frWrapper);
275 - return true;
276 } else { 273 } else {
277 - tableEntryService.unbindEntryReference(entryRef); 274 + tableEntryService.unbind(entryRef);
278 - return null;
279 } 275 }
280 - }); 276 + }
281 } 277 }
282 278
283 return processedFlowRules; 279 return processedFlowRules;
...@@ -287,7 +283,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -287,7 +283,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
287 try { 283 try {
288 return agent.addTableEntry(entry); 284 return agent.addTableEntry(entry);
289 } catch (Bmv2RuntimeException e) { 285 } catch (Bmv2RuntimeException e) {
290 - if (e.getCode() != TABLE_DUPLICATE_ENTRY) { 286 + if (e.getCode().equals(TABLE_DUPLICATE_ENTRY)) {
291 forceRemove(agent, entry.tableName(), entry.matchKey()); 287 forceRemove(agent, entry.tableName(), entry.matchKey());
292 return agent.addTableEntry(entry); 288 return agent.addTableEntry(entry);
293 } else { 289 } else {
...@@ -301,7 +297,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -301,7 +297,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
301 try { 297 try {
302 agent.deleteTableEntry(tableName, entryId); 298 agent.deleteTableEntry(tableName, entryId);
303 } catch (Bmv2RuntimeException e) { 299 } catch (Bmv2RuntimeException e) {
304 - if (e.getCode() == TABLE_INVALID_HANDLE || e.getCode() == TABLE_EXPIRED_HANDLE) { 300 + if (e.getCode().equals(TABLE_INVALID_HANDLE) || e.getCode().equals(TABLE_EXPIRED_HANDLE)) {
305 // entry is not there with the declared ID, try with a forced remove. 301 // entry is not there with the declared ID, try with a forced remove.
306 forceRemove(agent, tableName, matchKey); 302 forceRemove(agent, tableName, matchKey);
307 } else { 303 } else {
......
...@@ -21,6 +21,7 @@ import com.google.common.annotations.Beta; ...@@ -21,6 +21,7 @@ import com.google.common.annotations.Beta;
21 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator; 21 import org.onosproject.bmv2.api.context.Bmv2FlowRuleTranslator;
22 import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper; 22 import org.onosproject.bmv2.api.runtime.Bmv2FlowRuleWrapper;
23 import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference; 23 import org.onosproject.bmv2.api.runtime.Bmv2TableEntryReference;
24 +import org.onosproject.net.DeviceId;
24 25
25 /** 26 /**
26 * A service for managing BMv2 table entries. 27 * A service for managing BMv2 table entries.
...@@ -41,7 +42,7 @@ public interface Bmv2TableEntryService { ...@@ -41,7 +42,7 @@ public interface Bmv2TableEntryService {
41 * @param entryRef a table entry reference 42 * @param entryRef a table entry reference
42 * @param rule a BMv2 flow rule wrapper 43 * @param rule a BMv2 flow rule wrapper
43 */ 44 */
44 - void bindEntryReference(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule); 45 + void bind(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule);
45 46
46 /** 47 /**
47 * Returns the ONOS flow rule associated with the given BMv2 table entry reference, or null if there's no such a 48 * Returns the ONOS flow rule associated with the given BMv2 table entry reference, or null if there's no such a
...@@ -50,12 +51,19 @@ public interface Bmv2TableEntryService { ...@@ -50,12 +51,19 @@ public interface Bmv2TableEntryService {
50 * @param entryRef a table entry reference 51 * @param entryRef a table entry reference
51 * @return a BMv2 flow rule wrapper 52 * @return a BMv2 flow rule wrapper
52 */ 53 */
53 - Bmv2FlowRuleWrapper lookupEntryReference(Bmv2TableEntryReference entryRef); 54 + Bmv2FlowRuleWrapper lookup(Bmv2TableEntryReference entryRef);
54 55
55 /** 56 /**
56 * Removes any flow rule previously bound with a given BMv2 table entry reference. 57 * Removes any flow rule previously bound with a given BMv2 table entry reference.
57 * 58 *
58 * @param entryRef a table entry reference 59 * @param entryRef a table entry reference
59 */ 60 */
60 - void unbindEntryReference(Bmv2TableEntryReference entryRef); 61 + void unbind(Bmv2TableEntryReference entryRef);
62 +
63 + /**
64 + * Removes all bindings for a given device.
65 + *
66 + * @param deviceId a device ID
67 + */
68 + void unbindAll(DeviceId deviceId);
61 } 69 }
......
...@@ -34,6 +34,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam; ...@@ -34,6 +34,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2ValidMatchParam;
34 import org.onosproject.bmv2.api.service.Bmv2Controller; 34 import org.onosproject.bmv2.api.service.Bmv2Controller;
35 import org.onosproject.bmv2.api.service.Bmv2DeviceContextService; 35 import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
36 import org.onosproject.bmv2.api.service.Bmv2TableEntryService; 36 import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
37 +import org.onosproject.net.DeviceId;
37 import org.onosproject.store.serializers.KryoNamespaces; 38 import org.onosproject.store.serializers.KryoNamespaces;
38 import org.onosproject.store.service.EventuallyConsistentMap; 39 import org.onosproject.store.service.EventuallyConsistentMap;
39 import org.onosproject.store.service.StorageService; 40 import org.onosproject.store.service.StorageService;
...@@ -99,21 +100,29 @@ public class Bmv2TableEntryServiceImpl implements Bmv2TableEntryService { ...@@ -99,21 +100,29 @@ public class Bmv2TableEntryServiceImpl implements Bmv2TableEntryService {
99 } 100 }
100 101
101 @Override 102 @Override
102 - public Bmv2FlowRuleWrapper lookupEntryReference(Bmv2TableEntryReference entryRef) { 103 + public Bmv2FlowRuleWrapper lookup(Bmv2TableEntryReference entryRef) {
103 checkNotNull(entryRef, "table entry reference cannot be null"); 104 checkNotNull(entryRef, "table entry reference cannot be null");
104 return flowRules.get(entryRef); 105 return flowRules.get(entryRef);
105 } 106 }
106 107
107 @Override 108 @Override
108 - public void bindEntryReference(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule) { 109 + public void bind(Bmv2TableEntryReference entryRef, Bmv2FlowRuleWrapper rule) {
109 checkNotNull(entryRef, "table entry reference cannot be null"); 110 checkNotNull(entryRef, "table entry reference cannot be null");
110 checkNotNull(rule, "bmv2 flow rule cannot be null"); 111 checkNotNull(rule, "bmv2 flow rule cannot be null");
111 flowRules.put(entryRef, rule); 112 flowRules.put(entryRef, rule);
112 } 113 }
113 114
114 @Override 115 @Override
115 - public void unbindEntryReference(Bmv2TableEntryReference entryRef) { 116 + public void unbind(Bmv2TableEntryReference entryRef) {
116 checkNotNull(entryRef, "table entry reference cannot be null"); 117 checkNotNull(entryRef, "table entry reference cannot be null");
117 flowRules.remove(entryRef); 118 flowRules.remove(entryRef);
118 } 119 }
120 +
121 + @Override
122 + public void unbindAll(DeviceId deviceId) {
123 + flowRules.keySet()
124 + .stream()
125 + .filter(entryRef -> entryRef.deviceId().equals(deviceId))
126 + .forEach(flowRules::remove);
127 + }
119 } 128 }
......
...@@ -29,6 +29,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException; ...@@ -29,6 +29,7 @@ import org.onosproject.bmv2.api.runtime.Bmv2RuntimeException;
29 import org.onosproject.bmv2.api.service.Bmv2Controller; 29 import org.onosproject.bmv2.api.service.Bmv2Controller;
30 import org.onosproject.bmv2.api.service.Bmv2DeviceContextService; 30 import org.onosproject.bmv2.api.service.Bmv2DeviceContextService;
31 import org.onosproject.bmv2.api.service.Bmv2DeviceListener; 31 import org.onosproject.bmv2.api.service.Bmv2DeviceListener;
32 +import org.onosproject.bmv2.api.service.Bmv2TableEntryService;
32 import org.onosproject.common.net.AbstractDeviceProvider; 33 import org.onosproject.common.net.AbstractDeviceProvider;
33 import org.onosproject.core.ApplicationId; 34 import org.onosproject.core.ApplicationId;
34 import org.onosproject.core.CoreService; 35 import org.onosproject.core.CoreService;
...@@ -107,6 +108,9 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -107,6 +108,9 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected Bmv2DeviceContextService contextService; 109 protected Bmv2DeviceContextService contextService;
109 110
111 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 + protected Bmv2TableEntryService tableEntryService;
113 +
110 private ApplicationId appId; 114 private ApplicationId appId;
111 115
112 /** 116 /**
...@@ -228,6 +232,8 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -228,6 +232,8 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
228 private void resetDeviceState(DeviceId did) { 232 private void resetDeviceState(DeviceId did) {
229 try { 233 try {
230 controller.getAgent(did).resetState(); 234 controller.getAgent(did).resetState();
235 + // Tables emptied. Reset all bindings.
236 + tableEntryService.unbindAll(did);
231 } catch (Bmv2RuntimeException e) { 237 } catch (Bmv2RuntimeException e) {
232 log.warn("Unable to reset {}: {}", did, e.toString()); 238 log.warn("Unable to reset {}: {}", did, e.toString());
233 } 239 }
......