|
@@ -3144,22 +3144,60 @@ out:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void ocfs2_process_blocked_lock(struct ocfs2_super *osb,
|
|
|
+ struct ocfs2_lock_res *lockres);
|
|
|
+
|
|
|
/* Mark the lockres as being dropped. It will no longer be
|
|
|
* queued if blocking, but we still may have to wait on it
|
|
|
* being dequeued from the downconvert thread before we can consider
|
|
|
* it safe to drop.
|
|
|
*
|
|
|
* You can *not* attempt to call cluster_lock on this lockres anymore. */
|
|
|
-void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres)
|
|
|
+void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
|
|
|
+ struct ocfs2_lock_res *lockres)
|
|
|
{
|
|
|
int status;
|
|
|
struct ocfs2_mask_waiter mw;
|
|
|
- unsigned long flags;
|
|
|
+ unsigned long flags, flags2;
|
|
|
|
|
|
ocfs2_init_mask_waiter(&mw);
|
|
|
|
|
|
spin_lock_irqsave(&lockres->l_lock, flags);
|
|
|
lockres->l_flags |= OCFS2_LOCK_FREEING;
|
|
|
+ if (lockres->l_flags & OCFS2_LOCK_QUEUED && current == osb->dc_task) {
|
|
|
+ /*
|
|
|
+ * We know the downconvert is queued but not in progress
|
|
|
+ * because we are the downconvert thread and processing
|
|
|
+ * different lock. So we can just remove the lock from the
|
|
|
+ * queue. This is not only an optimization but also a way
|
|
|
+ * to avoid the following deadlock:
|
|
|
+ * ocfs2_dentry_post_unlock()
|
|
|
+ * ocfs2_dentry_lock_put()
|
|
|
+ * ocfs2_drop_dentry_lock()
|
|
|
+ * iput()
|
|
|
+ * ocfs2_evict_inode()
|
|
|
+ * ocfs2_clear_inode()
|
|
|
+ * ocfs2_mark_lockres_freeing()
|
|
|
+ * ... blocks waiting for OCFS2_LOCK_QUEUED
|
|
|
+ * since we are the downconvert thread which
|
|
|
+ * should clear the flag.
|
|
|
+ */
|
|
|
+ spin_unlock_irqrestore(&lockres->l_lock, flags);
|
|
|
+ spin_lock_irqsave(&osb->dc_task_lock, flags2);
|
|
|
+ list_del_init(&lockres->l_blocked_list);
|
|
|
+ osb->blocked_lock_count--;
|
|
|
+ spin_unlock_irqrestore(&osb->dc_task_lock, flags2);
|
|
|
+ /*
|
|
|
+ * Warn if we recurse into another post_unlock call. Strictly
|
|
|
+ * speaking it isn't a problem but we need to be careful if
|
|
|
+ * that happens (stack overflow, deadlocks, ...) so warn if
|
|
|
+ * ocfs2 grows a path for which this can happen.
|
|
|
+ */
|
|
|
+ WARN_ON_ONCE(lockres->l_ops->post_unlock);
|
|
|
+ /* Since the lock is freeing we don't do much in the fn below */
|
|
|
+ ocfs2_process_blocked_lock(osb, lockres);
|
|
|
+ return;
|
|
|
+ }
|
|
|
while (lockres->l_flags & OCFS2_LOCK_QUEUED) {
|
|
|
lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_QUEUED, 0);
|
|
|
spin_unlock_irqrestore(&lockres->l_lock, flags);
|
|
@@ -3180,7 +3218,7 @@ void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
- ocfs2_mark_lockres_freeing(lockres);
|
|
|
+ ocfs2_mark_lockres_freeing(osb, lockres);
|
|
|
ret = ocfs2_drop_lock(osb, lockres);
|
|
|
if (ret)
|
|
|
mlog_errno(ret);
|