Committed by
Gerrit Code Review
Fixed deadlock in BMv2 device context service
Also minor refactoring of synchronized blocks. Change-Id: Ifea25208ca4f1839bb3f21ba5b5ecfb2441baa35
Showing
3 changed files
with
35 additions
and
26 deletions
... | @@ -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) { |
175 | + if (storedContext == null) { | ||
176 | + return; | ||
177 | + } | ||
173 | // Synchronize executions over the same deviceId. | 178 | // Synchronize executions over the same deviceId. |
174 | - deviceLocks.putIfAbsent(deviceId, new Boolean(true)); | 179 | + Lock lock = deviceLocks.computeIfAbsent(deviceId, did -> new ReentrantLock()); |
175 | - synchronized (deviceLocks.get(deviceId)) { | 180 | + lock.lock(); |
176 | - | 181 | + try { |
177 | - Bmv2DeviceContext storedContext = getContext(deviceId); | ||
178 | - if (storedContext == null) { | ||
179 | - return; | ||
180 | - } | ||
181 | - | ||
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) -> { | 272 | + if (deviceService.isAvailable(did)) { |
273 | - if (deviceService.isAvailable(did)) { | 273 | + providerService.deviceDisconnected(did); |
274 | - providerService.deviceDisconnected(did); | 274 | + } |
275 | - } | 275 | + activeDevices.put(did, null); |
276 | - return null; | ||
277 | - }); | ||
278 | } | 276 | } |
279 | 277 | ||
280 | /** | 278 | /** | ... | ... |
-
Please register or login to post a comment