Commit ddc09c8d authored by Kurt Hackel's avatar Kurt Hackel Committed by Mark Fasheh

ocfs2_dlm: Fixes race between migrate and dirty

dlmthread was removing lockres' from the dirty list
and resetting the dirty flag before shuffling the list.
This patch retains the dirty state flag until the lists
are shuffled.
Signed-off-by: default avatarKurt Hackel <kurt.hackel@oracle.com>
Signed-off-by: default avatarSunil Mushran <Sunil.Mushran@oracle.com>
Signed-off-by: default avatarMark Fasheh <mark.fasheh@oracle.com>
parent faf0ec9f
...@@ -223,6 +223,7 @@ static inline void __dlm_set_joining_node(struct dlm_ctxt *dlm, ...@@ -223,6 +223,7 @@ static inline void __dlm_set_joining_node(struct dlm_ctxt *dlm,
#define DLM_LOCK_RES_IN_PROGRESS 0x00000010 #define DLM_LOCK_RES_IN_PROGRESS 0x00000010
#define DLM_LOCK_RES_MIGRATING 0x00000020 #define DLM_LOCK_RES_MIGRATING 0x00000020
#define DLM_LOCK_RES_DROPPING_REF 0x00000040 #define DLM_LOCK_RES_DROPPING_REF 0x00000040
#define DLM_LOCK_RES_BLOCK_DIRTY 0x00001000
/* max milliseconds to wait to sync up a network failure with a node death */ /* max milliseconds to wait to sync up a network failure with a node death */
#define DLM_NODE_DEATH_WAIT_MAX (5 * 1000) #define DLM_NODE_DEATH_WAIT_MAX (5 * 1000)
......
...@@ -2707,8 +2707,15 @@ static int dlm_mark_lockres_migrating(struct dlm_ctxt *dlm, ...@@ -2707,8 +2707,15 @@ static int dlm_mark_lockres_migrating(struct dlm_ctxt *dlm,
__dlm_lockres_reserve_ast(res); __dlm_lockres_reserve_ast(res);
spin_unlock(&res->spinlock); spin_unlock(&res->spinlock);
/* now flush all the pending asts.. hang out for a bit */ /* now flush all the pending asts */
dlm_kick_thread(dlm, res); dlm_kick_thread(dlm, res);
/* before waiting on DIRTY, block processes which may
* try to dirty the lockres before MIGRATING is set */
spin_lock(&res->spinlock);
BUG_ON(res->state & DLM_LOCK_RES_BLOCK_DIRTY);
res->state |= DLM_LOCK_RES_BLOCK_DIRTY;
spin_unlock(&res->spinlock);
/* now wait on any pending asts and the DIRTY state */
wait_event(dlm->ast_wq, !dlm_lockres_is_dirty(dlm, res)); wait_event(dlm->ast_wq, !dlm_lockres_is_dirty(dlm, res));
dlm_lockres_release_ast(dlm, res); dlm_lockres_release_ast(dlm, res);
...@@ -2734,6 +2741,13 @@ static int dlm_mark_lockres_migrating(struct dlm_ctxt *dlm, ...@@ -2734,6 +2741,13 @@ static int dlm_mark_lockres_migrating(struct dlm_ctxt *dlm,
mlog(0, "trying again...\n"); mlog(0, "trying again...\n");
goto again; goto again;
} }
/* now that we are sure the MIGRATING state is there, drop
* the unneded state which blocked threads trying to DIRTY */
spin_lock(&res->spinlock);
BUG_ON(!(res->state & DLM_LOCK_RES_BLOCK_DIRTY));
BUG_ON(!(res->state & DLM_LOCK_RES_MIGRATING));
res->state &= ~DLM_LOCK_RES_BLOCK_DIRTY;
spin_unlock(&res->spinlock);
/* did the target go down or die? */ /* did the target go down or die? */
spin_lock(&dlm->spinlock); spin_lock(&dlm->spinlock);
......
...@@ -95,7 +95,7 @@ int __dlm_lockres_has_locks(struct dlm_lock_resource *res) ...@@ -95,7 +95,7 @@ int __dlm_lockres_has_locks(struct dlm_lock_resource *res)
int __dlm_lockres_unused(struct dlm_lock_resource *res) int __dlm_lockres_unused(struct dlm_lock_resource *res)
{ {
if (!__dlm_lockres_has_locks(res) && if (!__dlm_lockres_has_locks(res) &&
list_empty(&res->dirty)) { (list_empty(&res->dirty) && !(res->state & DLM_LOCK_RES_DIRTY))) {
/* try not to scan the bitmap unless the first two /* try not to scan the bitmap unless the first two
* conditions are already true */ * conditions are already true */
int bit = find_next_bit(res->refmap, O2NM_MAX_NODES, 0); int bit = find_next_bit(res->refmap, O2NM_MAX_NODES, 0);
...@@ -455,13 +455,18 @@ void __dlm_dirty_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res) ...@@ -455,13 +455,18 @@ void __dlm_dirty_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res)
assert_spin_locked(&res->spinlock); assert_spin_locked(&res->spinlock);
/* don't shuffle secondary queues */ /* don't shuffle secondary queues */
if ((res->owner == dlm->node_num) && if ((res->owner == dlm->node_num)) {
!(res->state & DLM_LOCK_RES_DIRTY)) { if (res->state & (DLM_LOCK_RES_MIGRATING |
DLM_LOCK_RES_BLOCK_DIRTY))
return;
if (list_empty(&res->dirty)) {
/* ref for dirty_list */ /* ref for dirty_list */
dlm_lockres_get(res); dlm_lockres_get(res);
list_add_tail(&res->dirty, &dlm->dirty_list); list_add_tail(&res->dirty, &dlm->dirty_list);
res->state |= DLM_LOCK_RES_DIRTY; res->state |= DLM_LOCK_RES_DIRTY;
} }
}
} }
...@@ -639,7 +644,7 @@ static int dlm_thread(void *data) ...@@ -639,7 +644,7 @@ static int dlm_thread(void *data)
dlm_lockres_get(res); dlm_lockres_get(res);
spin_lock(&res->spinlock); spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_DIRTY; /* We clear the DLM_LOCK_RES_DIRTY state once we shuffle lists below */
list_del_init(&res->dirty); list_del_init(&res->dirty);
spin_unlock(&res->spinlock); spin_unlock(&res->spinlock);
spin_unlock(&dlm->spinlock); spin_unlock(&dlm->spinlock);
...@@ -663,10 +668,11 @@ static int dlm_thread(void *data) ...@@ -663,10 +668,11 @@ static int dlm_thread(void *data)
/* it is now ok to move lockreses in these states /* it is now ok to move lockreses in these states
* to the dirty list, assuming that they will only be * to the dirty list, assuming that they will only be
* dirty for a short while. */ * dirty for a short while. */
BUG_ON(res->state & DLM_LOCK_RES_MIGRATING);
if (res->state & (DLM_LOCK_RES_IN_PROGRESS | if (res->state & (DLM_LOCK_RES_IN_PROGRESS |
DLM_LOCK_RES_MIGRATING |
DLM_LOCK_RES_RECOVERING)) { DLM_LOCK_RES_RECOVERING)) {
/* move it to the tail and keep going */ /* move it to the tail and keep going */
res->state &= ~DLM_LOCK_RES_DIRTY;
spin_unlock(&res->spinlock); spin_unlock(&res->spinlock);
mlog(0, "delaying list shuffling for in-" mlog(0, "delaying list shuffling for in-"
"progress lockres %.*s, state=%d\n", "progress lockres %.*s, state=%d\n",
...@@ -687,6 +693,7 @@ static int dlm_thread(void *data) ...@@ -687,6 +693,7 @@ static int dlm_thread(void *data)
/* called while holding lockres lock */ /* called while holding lockres lock */
dlm_shuffle_lists(dlm, res); dlm_shuffle_lists(dlm, res);
res->state &= ~DLM_LOCK_RES_DIRTY;
spin_unlock(&res->spinlock); spin_unlock(&res->spinlock);
dlm_lockres_calc_usage(dlm, res); dlm_lockres_calc_usage(dlm, res);
...@@ -697,11 +704,8 @@ static int dlm_thread(void *data) ...@@ -697,11 +704,8 @@ static int dlm_thread(void *data)
/* if the lock was in-progress, stick /* if the lock was in-progress, stick
* it on the back of the list */ * it on the back of the list */
if (delay) { if (delay) {
/* ref for dirty_list */
dlm_lockres_get(res);
spin_lock(&res->spinlock); spin_lock(&res->spinlock);
list_add_tail(&res->dirty, &dlm->dirty_list); __dlm_dirty_lockres(dlm, res);
res->state |= DLM_LOCK_RES_DIRTY;
spin_unlock(&res->spinlock); spin_unlock(&res->spinlock);
} }
dlm_lockres_put(res); dlm_lockres_put(res);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment