Carmelo Cascone
Committed by Jonathan Hart

Fixed deadlock in BMv2 device context service

Also minor refactoring of synchronized blocks.

Change-Id: Ifea25208ca4f1839bb3f21ba5b5ecfb2441baa35
...@@ -49,6 +49,8 @@ import java.util.Collections; ...@@ -49,6 +49,8 @@ import java.util.Collections;
49 import java.util.Date; 49 import java.util.Date;
50 import java.util.List; 50 import java.util.List;
51 import java.util.concurrent.ConcurrentMap; 51 import java.util.concurrent.ConcurrentMap;
52 +import java.util.concurrent.locks.Lock;
53 +import java.util.concurrent.locks.ReentrantLock;
52 54
53 import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*; 55 import static org.onosproject.bmv2.api.runtime.Bmv2RuntimeException.Code.*;
54 import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED; 56 import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
...@@ -61,7 +63,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -61,7 +63,7 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
61 private final Logger log = LoggerFactory.getLogger(this.getClass()); 63 private final Logger log = LoggerFactory.getLogger(this.getClass());
62 64
63 // Needed to synchronize operations over the same table entry. 65 // Needed to synchronize operations over the same table entry.
64 - private static final ConcurrentMap<Bmv2TableEntryReference, Boolean> ENTRY_LOCKS = Maps.newConcurrentMap(); 66 + private static final ConcurrentMap<Bmv2TableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
65 67
66 private Bmv2Controller controller; 68 private Bmv2Controller controller;
67 private Bmv2TableEntryService tableEntryService; 69 private Bmv2TableEntryService tableEntryService;
...@@ -131,9 +133,10 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -131,9 +133,10 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
131 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, table.name(), 133 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, table.name(),
132 parsedEntry.matchKey()); 134 parsedEntry.matchKey());
133 135
134 - ENTRY_LOCKS.putIfAbsent(entryRef, true); 136 + Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, key -> new ReentrantLock());
135 - synchronized (ENTRY_LOCKS.get(entryRef)) { 137 + lock.lock();
136 138
139 + try {
137 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef); 140 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
138 141
139 if (frWrapper == null) { 142 if (frWrapper == null) {
...@@ -173,6 +176,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -173,6 +176,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
173 FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(), 176 FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
174 packets, bytes); 177 packets, bytes);
175 entryList.add(entry); 178 entryList.add(entry);
179 +
180 + } finally {
181 + lock.unlock();
176 } 182 }
177 } 183 }
178 } 184 }
...@@ -232,8 +238,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -232,8 +238,9 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
232 String tableName = bmv2Entry.tableName(); 238 String tableName = bmv2Entry.tableName();
233 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey()); 239 Bmv2TableEntryReference entryRef = new Bmv2TableEntryReference(deviceId, tableName, bmv2Entry.matchKey());
234 240
235 - ENTRY_LOCKS.putIfAbsent(entryRef, true); 241 + Lock lock = ENTRY_LOCKS.computeIfAbsent(entryRef, k -> new ReentrantLock());
236 - synchronized (ENTRY_LOCKS.get(entryRef)) { 242 + lock.lock();
243 + try {
237 // Get from store 244 // Get from store
238 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef); 245 Bmv2FlowRuleWrapper frWrapper = tableEntryService.lookup(entryRef);
239 try { 246 try {
...@@ -273,6 +280,8 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement ...@@ -273,6 +280,8 @@ public class Bmv2FlowRuleProgrammable extends AbstractHandlerBehaviour implement
273 } else { 280 } else {
274 tableEntryService.unbind(entryRef); 281 tableEntryService.unbind(entryRef);
275 } 282 }
283 + } finally {
284 + lock.unlock();
276 } 285 }
277 } 286 }
278 287
......
...@@ -60,6 +60,8 @@ import java.util.concurrent.ConcurrentMap; ...@@ -60,6 +60,8 @@ import java.util.concurrent.ConcurrentMap;
60 import java.util.concurrent.ScheduledExecutorService; 60 import java.util.concurrent.ScheduledExecutorService;
61 import java.util.concurrent.ScheduledFuture; 61 import java.util.concurrent.ScheduledFuture;
62 import java.util.concurrent.TimeUnit; 62 import java.util.concurrent.TimeUnit;
63 +import java.util.concurrent.locks.Lock;
64 +import java.util.concurrent.locks.ReentrantLock;
63 65
64 import static com.google.common.base.Preconditions.checkNotNull; 66 import static com.google.common.base.Preconditions.checkNotNull;
65 import static org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration.parse; 67 import static org.onosproject.bmv2.api.context.Bmv2DefaultConfiguration.parse;
...@@ -87,7 +89,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { ...@@ -87,7 +89,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
87 89
88 private final ScheduledExecutorService scheduledExecutor = SharedScheduledExecutors.getPoolThreadExecutor(); 90 private final ScheduledExecutorService scheduledExecutor = SharedScheduledExecutors.getPoolThreadExecutor();
89 private final MapEventListener<DeviceId, Bmv2DeviceContext> contextListener = new ContextMapEventListener(); 91 private final MapEventListener<DeviceId, Bmv2DeviceContext> contextListener = new ContextMapEventListener();
90 - private final ConcurrentMap<DeviceId, Boolean> deviceLocks = Maps.newConcurrentMap(); 92 + private final ConcurrentMap<DeviceId, Lock> deviceLocks = Maps.newConcurrentMap();
91 93
92 private ConsistentMap<DeviceId, Bmv2DeviceContext> contexts; 94 private ConsistentMap<DeviceId, Bmv2DeviceContext> contexts;
93 private Map<String, ClassLoader> interpreterClassLoaders; 95 private Map<String, ClassLoader> interpreterClassLoaders;
...@@ -115,7 +117,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { ...@@ -115,7 +117,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
115 interpreterClassLoaders = Maps.newConcurrentMap(); 117 interpreterClassLoaders = Maps.newConcurrentMap();
116 registerInterpreterClassLoader(defaultInterpreter.getClass(), this.getClass().getClassLoader()); 118 registerInterpreterClassLoader(defaultInterpreter.getClass(), this.getClass().getClassLoader());
117 119
118 - contexts.addListener(contextListener); 120 + contexts.addListener(contextListener, scheduledExecutor);
119 121
120 if (configChecker != null && configChecker.isCancelled()) { 122 if (configChecker != null && configChecker.isCancelled()) {
121 configChecker.cancel(false); 123 configChecker.cancel(false);
...@@ -169,16 +171,14 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { ...@@ -169,16 +171,14 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
169 return defaultContext; 171 return defaultContext;
170 } 172 }
171 173
172 - private void configCheck(DeviceId deviceId) { 174 + private void configCheck(DeviceId deviceId, Bmv2DeviceContext storedContext) {
173 - // Synchronize executions over the same deviceId.
174 - deviceLocks.putIfAbsent(deviceId, new Boolean(true));
175 - synchronized (deviceLocks.get(deviceId)) {
176 -
177 - Bmv2DeviceContext storedContext = getContext(deviceId);
178 if (storedContext == null) { 175 if (storedContext == null) {
179 return; 176 return;
180 } 177 }
181 - 178 + // Synchronize executions over the same deviceId.
179 + Lock lock = deviceLocks.computeIfAbsent(deviceId, did -> new ReentrantLock());
180 + lock.lock();
181 + try {
182 log.trace("Executing configuration check on {}...", deviceId); 182 log.trace("Executing configuration check on {}...", deviceId);
183 183
184 try { 184 try {
...@@ -200,18 +200,20 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { ...@@ -200,18 +200,20 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
200 } catch (Bmv2RuntimeException e) { 200 } catch (Bmv2RuntimeException e) {
201 log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain()); 201 log.warn("Unable to dump JSON configuration from {}: {}", deviceId, e.explain());
202 } 202 }
203 + } finally {
204 + lock.unlock();
203 } 205 }
204 } 206 }
205 207
206 - private void triggerConfigCheck(DeviceId deviceId) { 208 + private void triggerConfigCheck(DeviceId deviceId, Bmv2DeviceContext context) {
207 if (mastershipService.isLocalMaster(deviceId)) { 209 if (mastershipService.isLocalMaster(deviceId)) {
208 - scheduledExecutor.schedule(() -> configCheck(deviceId), 0, TimeUnit.SECONDS); 210 + scheduledExecutor.schedule(() -> configCheck(deviceId, context), 0, TimeUnit.SECONDS);
209 } 211 }
210 } 212 }
211 213
212 private void checkDevices() { 214 private void checkDevices() {
213 deviceService.getAvailableDevices().forEach(device -> { 215 deviceService.getAvailableDevices().forEach(device -> {
214 - triggerConfigCheck(device.id()); 216 + triggerConfigCheck(device.id(), getContext(device.id()));
215 }); 217 });
216 } 218 }
217 219
...@@ -234,7 +236,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService { ...@@ -234,7 +236,7 @@ public class Bmv2DeviceContextServiceImpl implements Bmv2DeviceContextService {
234 DeviceId deviceId = event.key(); 236 DeviceId deviceId = event.key();
235 if (event.type().equals(INSERT) || event.type().equals(UPDATE)) { 237 if (event.type().equals(INSERT) || event.type().equals(UPDATE)) {
236 log.trace("Context {} for {}", event.type().name(), deviceId); 238 log.trace("Context {} for {}", event.type().name(), deviceId);
237 - triggerConfigCheck(deviceId); 239 + triggerConfigCheck(deviceId, event.newValue().value());
238 } 240 }
239 } 241 }
240 } 242 }
......
...@@ -269,12 +269,10 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider { ...@@ -269,12 +269,10 @@ public class Bmv2DeviceProvider extends AbstractDeviceProvider {
269 269
270 private void disconnectDevice(DeviceId did) { 270 private void disconnectDevice(DeviceId did) {
271 log.debug("Trying to disconnect device from core... deviceId={}", did); 271 log.debug("Trying to disconnect device from core... deviceId={}", did);
272 - activeDevices.compute(did, (k, v) -> {
273 if (deviceService.isAvailable(did)) { 272 if (deviceService.isAvailable(did)) {
274 providerService.deviceDisconnected(did); 273 providerService.deviceDisconnected(did);
275 } 274 }
276 - return null; 275 + activeDevices.put(did, null);
277 - });
278 } 276 }
279 277
280 /** 278 /**
......