Committed by
Jonathan Hart
Improved consistency for BMv2 flow rules handling
Change-Id: I3a4798af3f35f135e8162385a1bf7fc059028307
Showing
4 changed files
with
56 additions
and
37 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment