aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2
diff options
context:
space:
mode:
authorJunxiao Bi <junxiao.bi@oracle.com>2014-04-03 17:46:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:20:54 -0400
commit34aa8dac482f1358d59110d5e3a12f4351f6acaa (patch)
tree87094ee84e82c1981154be1d1637058b898d8dd5 /fs/ocfs2
parent2931cdcb49194503b19345c597b68fdcf78396f8 (diff)
ocfs2: dlm: fix lock migration crash
This issue was introduced by commit 800deef3f6f8 ("ocfs2: use list_for_each_entry where benefical") in 2007 where it replaced list_for_each with list_for_each_entry. The variable "lock" will point to invalid data if "tmpq" list is empty and a panic will be triggered due to this. Sunil advised reverting it back, but the old version was also not right. At the end of the outer for loop, that list_for_each_entry will also set "lock" to an invalid data, then in the next loop, if the "tmpq" list is empty, "lock" will be an stale invalid data and cause the panic. So reverting the list_for_each back and reset "lock" to NULL to fix this issue. Another concern is that this seemes can not happen because the "tmpq" list should not be empty. Let me describe how. old lock resource owner(node 1): migratation target(node 2): image there's lockres with a EX lock from node 2 in granted list, a NR lock from node x with convert_type EX in converting list. dlm_empty_lockres() { dlm_pick_migration_target() { pick node 2 as target as its lock is the first one in granted list. } dlm_migrate_lockres() { dlm_mark_lockres_migrating() { res->state |= DLM_LOCK_RES_BLOCK_DIRTY; wait_event(dlm->ast_wq, !dlm_lockres_is_dirty(dlm, res)); //after the above code, we can not dirty lockres any more, // so dlm_thread shuffle list will not run downconvert lock from EX to NR upconvert lock from NR to EX <<< migration may schedule out here, then <<< node 2 send down convert request to convert type from EX to <<< NR, then send up convert request to convert type from NR to <<< EX, at this time, lockres granted list is empty, and two locks <<< in the converting list, node x up convert lock followed by <<< node 2 up convert lock. // will set lockres RES_MIGRATING flag, the following // lock/unlock can not run dlm_lockres_release_ast(dlm, res); } dlm_send_one_lockres() dlm_process_recovery_data() for (i=0; i<mres->num_locks; i++) if (ml->node == dlm->node_num) for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) { list_for_each_entry(lock, tmpq, list) if (lock) break; <<< lock is invalid as grant list is empty. } if (lock->ml.node != ml->node) BUG() >>> crash here } I see the above locks status from a vmcore of our internal bug. Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com> Reviewed-by: Wengang Wang <wen.gang.wang@oracle.com> Cc: Sunil Mushran <sunil.mushran@gmail.com> Reviewed-by: Srinivas Eeda <srinivas.eeda@oracle.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Mark Fasheh <mfasheh@suse.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c14
1 files changed, 8 insertions, 6 deletions
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 7035af09cc03..c2dd2589e04e 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1750,13 +1750,13 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
1750 struct dlm_migratable_lockres *mres) 1750 struct dlm_migratable_lockres *mres)
1751{ 1751{
1752 struct dlm_migratable_lock *ml; 1752 struct dlm_migratable_lock *ml;
1753 struct list_head *queue; 1753 struct list_head *queue, *iter;
1754 struct list_head *tmpq = NULL; 1754 struct list_head *tmpq = NULL;
1755 struct dlm_lock *newlock = NULL; 1755 struct dlm_lock *newlock = NULL;
1756 struct dlm_lockstatus *lksb = NULL; 1756 struct dlm_lockstatus *lksb = NULL;
1757 int ret = 0; 1757 int ret = 0;
1758 int i, j, bad; 1758 int i, j, bad;
1759 struct dlm_lock *lock = NULL; 1759 struct dlm_lock *lock;
1760 u8 from = O2NM_MAX_NODES; 1760 u8 from = O2NM_MAX_NODES;
1761 unsigned int added = 0; 1761 unsigned int added = 0;
1762 __be64 c; 1762 __be64 c;
@@ -1791,14 +1791,16 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
1791 /* MIGRATION ONLY! */ 1791 /* MIGRATION ONLY! */
1792 BUG_ON(!(mres->flags & DLM_MRES_MIGRATION)); 1792 BUG_ON(!(mres->flags & DLM_MRES_MIGRATION));
1793 1793
1794 lock = NULL;
1794 spin_lock(&res->spinlock); 1795 spin_lock(&res->spinlock);
1795 for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) { 1796 for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) {
1796 tmpq = dlm_list_idx_to_ptr(res, j); 1797 tmpq = dlm_list_idx_to_ptr(res, j);
1797 list_for_each_entry(lock, tmpq, list) { 1798 list_for_each(iter, tmpq) {
1798 if (lock->ml.cookie != ml->cookie) 1799 lock = list_entry(iter,
1799 lock = NULL; 1800 struct dlm_lock, list);
1800 else 1801 if (lock->ml.cookie == ml->cookie)
1801 break; 1802 break;
1803 lock = NULL;
1802 } 1804 }
1803 if (lock) 1805 if (lock)
1804 break; 1806 break;