diff options
Diffstat (limited to 'fs/ocfs2/dlm/userdlm.c')
-rw-r--r-- | fs/ocfs2/dlm/userdlm.c | 74 |
1 files changed, 56 insertions, 18 deletions
diff --git a/fs/ocfs2/dlm/userdlm.c b/fs/ocfs2/dlm/userdlm.c index c3764f4744ee..74ca4e5f9765 100644 --- a/fs/ocfs2/dlm/userdlm.c +++ b/fs/ocfs2/dlm/userdlm.c | |||
@@ -139,6 +139,10 @@ static void user_ast(void *opaque) | |||
139 | return; | 139 | return; |
140 | } | 140 | } |
141 | 141 | ||
142 | mlog_bug_on_msg(lockres->l_requested == LKM_IVMODE, | ||
143 | "Lockres %s, requested ivmode. flags 0x%x\n", | ||
144 | lockres->l_name, lockres->l_flags); | ||
145 | |||
142 | /* we're downconverting. */ | 146 | /* we're downconverting. */ |
143 | if (lockres->l_requested < lockres->l_level) { | 147 | if (lockres->l_requested < lockres->l_level) { |
144 | if (lockres->l_requested <= | 148 | if (lockres->l_requested <= |
@@ -229,23 +233,42 @@ static void user_unlock_ast(void *opaque, enum dlm_status status) | |||
229 | 233 | ||
230 | mlog(0, "UNLOCK AST called on lock %s\n", lockres->l_name); | 234 | mlog(0, "UNLOCK AST called on lock %s\n", lockres->l_name); |
231 | 235 | ||
232 | if (status != DLM_NORMAL) | 236 | if (status != DLM_NORMAL && status != DLM_CANCELGRANT) |
233 | mlog(ML_ERROR, "Dlm returns status %d\n", status); | 237 | mlog(ML_ERROR, "Dlm returns status %d\n", status); |
234 | 238 | ||
235 | spin_lock(&lockres->l_lock); | 239 | spin_lock(&lockres->l_lock); |
236 | if (lockres->l_flags & USER_LOCK_IN_TEARDOWN) | 240 | /* The teardown flag gets set early during the unlock process, |
241 | * so test the cancel flag to make sure that this ast isn't | ||
242 | * for a concurrent cancel. */ | ||
243 | if (lockres->l_flags & USER_LOCK_IN_TEARDOWN | ||
244 | && !(lockres->l_flags & USER_LOCK_IN_CANCEL)) { | ||
237 | lockres->l_level = LKM_IVMODE; | 245 | lockres->l_level = LKM_IVMODE; |
238 | else { | 246 | } else if (status == DLM_CANCELGRANT) { |
247 | mlog(0, "Lock %s, cancel fails, flags 0x%x\n", | ||
248 | lockres->l_name, lockres->l_flags); | ||
249 | /* We tried to cancel a convert request, but it was | ||
250 | * already granted. Don't clear the busy flag - the | ||
251 | * ast should've done this already. */ | ||
252 | BUG_ON(!(lockres->l_flags & USER_LOCK_IN_CANCEL)); | ||
253 | lockres->l_flags &= ~USER_LOCK_IN_CANCEL; | ||
254 | goto out_noclear; | ||
255 | } else { | ||
256 | BUG_ON(!(lockres->l_flags & USER_LOCK_IN_CANCEL)); | ||
257 | /* Cancel succeeded, we want to re-queue */ | ||
258 | mlog(0, "Lock %s, cancel succeeds, flags 0x%x\n", | ||
259 | lockres->l_name, lockres->l_flags); | ||
239 | lockres->l_requested = LKM_IVMODE; /* cancel an | 260 | lockres->l_requested = LKM_IVMODE; /* cancel an |
240 | * upconvert | 261 | * upconvert |
241 | * request. */ | 262 | * request. */ |
242 | lockres->l_flags &= ~USER_LOCK_IN_CANCEL; | 263 | lockres->l_flags &= ~USER_LOCK_IN_CANCEL; |
243 | /* we want the unblock thread to look at it again | 264 | /* we want the unblock thread to look at it again |
244 | * now. */ | 265 | * now. */ |
245 | __user_dlm_queue_lockres(lockres); | 266 | if (lockres->l_flags & USER_LOCK_BLOCKED) |
267 | __user_dlm_queue_lockres(lockres); | ||
246 | } | 268 | } |
247 | 269 | ||
248 | lockres->l_flags &= ~USER_LOCK_BUSY; | 270 | lockres->l_flags &= ~USER_LOCK_BUSY; |
271 | out_noclear: | ||
249 | spin_unlock(&lockres->l_lock); | 272 | spin_unlock(&lockres->l_lock); |
250 | 273 | ||
251 | wake_up(&lockres->l_event); | 274 | wake_up(&lockres->l_event); |
@@ -268,13 +291,26 @@ static void user_dlm_unblock_lock(void *opaque) | |||
268 | 291 | ||
269 | spin_lock(&lockres->l_lock); | 292 | spin_lock(&lockres->l_lock); |
270 | 293 | ||
271 | BUG_ON(!(lockres->l_flags & USER_LOCK_BLOCKED)); | 294 | mlog_bug_on_msg(!(lockres->l_flags & USER_LOCK_QUEUED), |
272 | BUG_ON(!(lockres->l_flags & USER_LOCK_QUEUED)); | 295 | "Lockres %s, flags 0x%x\n", |
296 | lockres->l_name, lockres->l_flags); | ||
273 | 297 | ||
274 | /* notice that we don't clear USER_LOCK_BLOCKED here. That's | 298 | /* notice that we don't clear USER_LOCK_BLOCKED here. If it's |
275 | * for user_ast to do. */ | 299 | * set, we want user_ast clear it. */ |
276 | lockres->l_flags &= ~USER_LOCK_QUEUED; | 300 | lockres->l_flags &= ~USER_LOCK_QUEUED; |
277 | 301 | ||
302 | /* It's valid to get here and no longer be blocked - if we get | ||
303 | * several basts in a row, we might be queued by the first | ||
304 | * one, the unblock thread might run and clear the queued | ||
305 | * flag, and finally we might get another bast which re-queues | ||
306 | * us before our ast for the downconvert is called. */ | ||
307 | if (!(lockres->l_flags & USER_LOCK_BLOCKED)) { | ||
308 | mlog(0, "Lockres %s, flags 0x%x: queued but not blocking\n", | ||
309 | lockres->l_name, lockres->l_flags); | ||
310 | spin_unlock(&lockres->l_lock); | ||
311 | goto drop_ref; | ||
312 | } | ||
313 | |||
278 | if (lockres->l_flags & USER_LOCK_IN_TEARDOWN) { | 314 | if (lockres->l_flags & USER_LOCK_IN_TEARDOWN) { |
279 | mlog(0, "lock is in teardown so we do nothing\n"); | 315 | mlog(0, "lock is in teardown so we do nothing\n"); |
280 | spin_unlock(&lockres->l_lock); | 316 | spin_unlock(&lockres->l_lock); |
@@ -282,7 +318,9 @@ static void user_dlm_unblock_lock(void *opaque) | |||
282 | } | 318 | } |
283 | 319 | ||
284 | if (lockres->l_flags & USER_LOCK_BUSY) { | 320 | if (lockres->l_flags & USER_LOCK_BUSY) { |
285 | mlog(0, "BUSY flag detected...\n"); | 321 | mlog(0, "Cancel lock %s, flags 0x%x\n", |
322 | lockres->l_name, lockres->l_flags); | ||
323 | |||
286 | if (lockres->l_flags & USER_LOCK_IN_CANCEL) { | 324 | if (lockres->l_flags & USER_LOCK_IN_CANCEL) { |
287 | spin_unlock(&lockres->l_lock); | 325 | spin_unlock(&lockres->l_lock); |
288 | goto drop_ref; | 326 | goto drop_ref; |
@@ -296,14 +334,7 @@ static void user_dlm_unblock_lock(void *opaque) | |||
296 | LKM_CANCEL, | 334 | LKM_CANCEL, |
297 | user_unlock_ast, | 335 | user_unlock_ast, |
298 | lockres); | 336 | lockres); |
299 | if (status == DLM_CANCELGRANT) { | 337 | if (status != DLM_NORMAL) |
300 | /* If we got this, then the ast was fired | ||
301 | * before we could cancel. We cleanup our | ||
302 | * state, and restart the function. */ | ||
303 | spin_lock(&lockres->l_lock); | ||
304 | lockres->l_flags &= ~USER_LOCK_IN_CANCEL; | ||
305 | spin_unlock(&lockres->l_lock); | ||
306 | } else if (status != DLM_NORMAL) | ||
307 | user_log_dlm_error("dlmunlock", status, lockres); | 338 | user_log_dlm_error("dlmunlock", status, lockres); |
308 | goto drop_ref; | 339 | goto drop_ref; |
309 | } | 340 | } |
@@ -581,6 +612,14 @@ int user_dlm_destroy_lock(struct user_lock_res *lockres) | |||
581 | mlog(0, "asked to destroy %s\n", lockres->l_name); | 612 | mlog(0, "asked to destroy %s\n", lockres->l_name); |
582 | 613 | ||
583 | spin_lock(&lockres->l_lock); | 614 | spin_lock(&lockres->l_lock); |
615 | if (lockres->l_flags & USER_LOCK_IN_TEARDOWN) { | ||
616 | mlog(0, "Lock is already torn down\n"); | ||
617 | spin_unlock(&lockres->l_lock); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | lockres->l_flags |= USER_LOCK_IN_TEARDOWN; | ||
622 | |||
584 | while (lockres->l_flags & USER_LOCK_BUSY) { | 623 | while (lockres->l_flags & USER_LOCK_BUSY) { |
585 | spin_unlock(&lockres->l_lock); | 624 | spin_unlock(&lockres->l_lock); |
586 | 625 | ||
@@ -606,7 +645,6 @@ int user_dlm_destroy_lock(struct user_lock_res *lockres) | |||
606 | 645 | ||
607 | lockres->l_flags &= ~USER_LOCK_ATTACHED; | 646 | lockres->l_flags &= ~USER_LOCK_ATTACHED; |
608 | lockres->l_flags |= USER_LOCK_BUSY; | 647 | lockres->l_flags |= USER_LOCK_BUSY; |
609 | lockres->l_flags |= USER_LOCK_IN_TEARDOWN; | ||
610 | spin_unlock(&lockres->l_lock); | 648 | spin_unlock(&lockres->l_lock); |
611 | 649 | ||
612 | mlog(0, "unlocking lockres %s\n", lockres->l_name); | 650 | mlog(0, "unlocking lockres %s\n", lockres->l_name); |