summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-04-03 17:46:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:20:55 -0400
commit84d86f83f9d0e8431a3c9eae4c47e9d7ff49a411 (patch)
treef8708771a3316e70468d98691d14d1873ae69213
parente3a767b60fd8a9f5e133f42f4970cff77ec43173 (diff)
ocfs2: avoid blocking in ocfs2_mark_lockres_freeing() in downconvert thread
If we are dropping last inode reference from downconvert thread, we will end up calling ocfs2_mark_lockres_freeing() which can block if the lock we are freeing is queued thus creating an A-A deadlock. Luckily, since we are the downconvert thread, we can immediately dequeue the lock and thus avoid waiting in this case. Signed-off-by: Jan Kara <jack@suse.cz> Reviewed-by: Mark Fasheh <mfasheh@suse.de> Reviewed-by: Srinivas Eeda <srinivas.eeda@oracle.com> Cc: Joel Becker <jlbec@evilplan.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/ocfs2/dlmglue.c44
-rw-r--r--fs/ocfs2/dlmglue.h3
-rw-r--r--fs/ocfs2/inode.c7
3 files changed, 47 insertions, 7 deletions
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 19986959d149..6bd690b5a061 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3144,22 +3144,60 @@ out:
3144 return 0; 3144 return 0;
3145} 3145}
3146 3146
3147static void ocfs2_process_blocked_lock(struct ocfs2_super *osb,
3148 struct ocfs2_lock_res *lockres);
3149
3147/* Mark the lockres as being dropped. It will no longer be 3150/* Mark the lockres as being dropped. It will no longer be
3148 * queued if blocking, but we still may have to wait on it 3151 * queued if blocking, but we still may have to wait on it
3149 * being dequeued from the downconvert thread before we can consider 3152 * being dequeued from the downconvert thread before we can consider
3150 * it safe to drop. 3153 * it safe to drop.
3151 * 3154 *
3152 * You can *not* attempt to call cluster_lock on this lockres anymore. */ 3155 * You can *not* attempt to call cluster_lock on this lockres anymore. */
3153void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres) 3156void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
3157 struct ocfs2_lock_res *lockres)
3154{ 3158{
3155 int status; 3159 int status;
3156 struct ocfs2_mask_waiter mw; 3160 struct ocfs2_mask_waiter mw;
3157 unsigned long flags; 3161 unsigned long flags, flags2;
3158 3162
3159 ocfs2_init_mask_waiter(&mw); 3163 ocfs2_init_mask_waiter(&mw);
3160 3164
3161 spin_lock_irqsave(&lockres->l_lock, flags); 3165 spin_lock_irqsave(&lockres->l_lock, flags);
3162 lockres->l_flags |= OCFS2_LOCK_FREEING; 3166 lockres->l_flags |= OCFS2_LOCK_FREEING;
3167 if (lockres->l_flags & OCFS2_LOCK_QUEUED && current == osb->dc_task) {
3168 /*
3169 * We know the downconvert is queued but not in progress
3170 * because we are the downconvert thread and processing
3171 * different lock. So we can just remove the lock from the
3172 * queue. This is not only an optimization but also a way
3173 * to avoid the following deadlock:
3174 * ocfs2_dentry_post_unlock()
3175 * ocfs2_dentry_lock_put()
3176 * ocfs2_drop_dentry_lock()
3177 * iput()
3178 * ocfs2_evict_inode()
3179 * ocfs2_clear_inode()
3180 * ocfs2_mark_lockres_freeing()
3181 * ... blocks waiting for OCFS2_LOCK_QUEUED
3182 * since we are the downconvert thread which
3183 * should clear the flag.
3184 */
3185 spin_unlock_irqrestore(&lockres->l_lock, flags);
3186 spin_lock_irqsave(&osb->dc_task_lock, flags2);
3187 list_del_init(&lockres->l_blocked_list);
3188 osb->blocked_lock_count--;
3189 spin_unlock_irqrestore(&osb->dc_task_lock, flags2);
3190 /*
3191 * Warn if we recurse into another post_unlock call. Strictly
3192 * speaking it isn't a problem but we need to be careful if
3193 * that happens (stack overflow, deadlocks, ...) so warn if
3194 * ocfs2 grows a path for which this can happen.
3195 */
3196 WARN_ON_ONCE(lockres->l_ops->post_unlock);
3197 /* Since the lock is freeing we don't do much in the fn below */
3198 ocfs2_process_blocked_lock(osb, lockres);
3199 return;
3200 }
3163 while (lockres->l_flags & OCFS2_LOCK_QUEUED) { 3201 while (lockres->l_flags & OCFS2_LOCK_QUEUED) {
3164 lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_QUEUED, 0); 3202 lockres_add_mask_waiter(lockres, &mw, OCFS2_LOCK_QUEUED, 0);
3165 spin_unlock_irqrestore(&lockres->l_lock, flags); 3203 spin_unlock_irqrestore(&lockres->l_lock, flags);
@@ -3180,7 +3218,7 @@ void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
3180{ 3218{
3181 int ret; 3219 int ret;
3182 3220
3183 ocfs2_mark_lockres_freeing(lockres); 3221 ocfs2_mark_lockres_freeing(osb, lockres);
3184 ret = ocfs2_drop_lock(osb, lockres); 3222 ret = ocfs2_drop_lock(osb, lockres);
3185 if (ret) 3223 if (ret)
3186 mlog_errno(ret); 3224 mlog_errno(ret);
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index 1d596d8c4a4a..d293a22c32c5 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -157,7 +157,8 @@ int ocfs2_refcount_lock(struct ocfs2_refcount_tree *ref_tree, int ex);
157void ocfs2_refcount_unlock(struct ocfs2_refcount_tree *ref_tree, int ex); 157void ocfs2_refcount_unlock(struct ocfs2_refcount_tree *ref_tree, int ex);
158 158
159 159
160void ocfs2_mark_lockres_freeing(struct ocfs2_lock_res *lockres); 160void ocfs2_mark_lockres_freeing(struct ocfs2_super *osb,
161 struct ocfs2_lock_res *lockres);
161void ocfs2_simple_drop_lockres(struct ocfs2_super *osb, 162void ocfs2_simple_drop_lockres(struct ocfs2_super *osb,
162 struct ocfs2_lock_res *lockres); 163 struct ocfs2_lock_res *lockres);
163 164
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 809b5d57a6b8..d437f3ba90b0 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -1080,6 +1080,7 @@ static void ocfs2_clear_inode(struct inode *inode)
1080{ 1080{
1081 int status; 1081 int status;
1082 struct ocfs2_inode_info *oi = OCFS2_I(inode); 1082 struct ocfs2_inode_info *oi = OCFS2_I(inode);
1083 struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
1083 1084
1084 clear_inode(inode); 1085 clear_inode(inode);
1085 trace_ocfs2_clear_inode((unsigned long long)oi->ip_blkno, 1086 trace_ocfs2_clear_inode((unsigned long long)oi->ip_blkno,
@@ -1096,9 +1097,9 @@ static void ocfs2_clear_inode(struct inode *inode)
1096 1097
1097 /* Do these before all the other work so that we don't bounce 1098 /* Do these before all the other work so that we don't bounce
1098 * the downconvert thread while waiting to destroy the locks. */ 1099 * the downconvert thread while waiting to destroy the locks. */
1099 ocfs2_mark_lockres_freeing(&oi->ip_rw_lockres); 1100 ocfs2_mark_lockres_freeing(osb, &oi->ip_rw_lockres);
1100 ocfs2_mark_lockres_freeing(&oi->ip_inode_lockres); 1101 ocfs2_mark_lockres_freeing(osb, &oi->ip_inode_lockres);
1101 ocfs2_mark_lockres_freeing(&oi->ip_open_lockres); 1102 ocfs2_mark_lockres_freeing(osb, &oi->ip_open_lockres);
1102 1103
1103 ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap, 1104 ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap,
1104 &oi->ip_la_data_resv); 1105 &oi->ip_la_data_resv);