ocfs2/dlm: do not purge lockres that is queued for assert master
PATCH 4/8 ocfs2/dlm: do not purge lockres that is queued for assert master
From: Xue jiufei <[email protected]>
Subject: ocfs2/dlm: do not purge lockres that is queued for assert master
When workqueue is delayed, it may occur that a lockres is purged while it is still queued for master assert. it may trigger BUG() as follows.
N1 N2dlm_get_lockres()
->dlm_do_master_requery
is the master of lockres, so queue assert_master work
dlm_thread() start running and purge the lockres
dlm_assert_master_worker() send assert master message to other nodes
receiving the assert_master
message, set master to N2
dlmlock_remote() send create_lock message to N2, but receive DLM_IVLOCKID, if it is RECOVERY lockres, it triggers the BUG().
Another BUG() is triggered when N3 become the new master and send assert_master to N1, N1 will trigger the BUG() because owner doesn't match. So we should not purge lockres when it is queued for assert master.
---
fs/ocfs2/dlm/dlmcommon.h | 4 +++ fs/ocfs2/dlm/dlmmaster.c | 43 ++++++++++++++++++++++++++++++++++- fs/ocfs2/dlm/dlmrecovery.c | 3 +- fs/ocfs2/dlm/dlmthread.c | 11 +++++--- 4 files changed, 55 insertions(+), 6 deletions(-)
fs/ocfs2/dlm/dlmcommon.h renamed from fs/ocfs2/dlm/dlmcommon.h~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +4 -0
@@ -332,6 +332,7 @@ struct dlm_lock_resourceu16 state;char lvb[DLM_LVB_LEN ];unsigned int inflight_locks; +unsigned int inflight_assert_workers;unsigned long refmap[BITS_TO_LONGS (O2NM_MAX_NODES )]; };
@@ -911,6 +912,9 @@ void dlm_lockres_drop_inflight_ref(strucvoid dlm_lockres_grab_inflight_ref (struct dlm_ctxt *dlm,struct dlm_lock_resource *res); +void __dlm_lockres_grab_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res); +void dlm_queue_ast (struct dlm_ctxt *dlm,struct dlm_lock *lock);void dlm_queue_bast (struct dlm_ctxt *dlm,struct dlm_lock *lock);void __dlm_queue_ast (struct dlm_ctxt *dlm,struct dlm_lock *lock);
fs/ocfs2/dlm/dlmmaster.c renamed from fs/ocfs2/dlm/dlmmaster.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +42 -1
67 lines not on this page · raw message
fs/ocfs2/dlm/dlmrecovery.c renamed from fs/ocfs2/dlm/dlmrecovery.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2 -1
@@ -1708,7 +1708,8 @@ int dlm_master_requery_handler(struct o2mlog_errno (-ENOMEM );/* retry!? */ BUG (); - } + }else +__dlm_lockres_grab_inflight_worker (dlm, res); }else /* put.. incase we are not the master */ dlm_lockres_put (res);spin_unlock (&res->spinlock);
fs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +7 -4
@@ -259,11 +259,14 @@ static void dlm_run_purge_list(struct dl* refs on it. */ unused =__dlm_lockres_unused (lockres);if (!unused || - (lockres->state &DLM_LOCK_RES_MIGRATING )) { + (lockres->state &DLM_LOCK_RES_MIGRATING ) || + (lockres->inflight_assert_workers !=0 )) {mlog (0 ,"%s: res %.*s is in use or being remastered, " -"used %d, state %d\n" , dlm->name, - lockres->lockname.len, lockres->lockname.name, - !unused, lockres->state); +"used %d, state %d, assert master workers %u\n" , + dlm->name, lockres->lockname.len, + lockres->lockname.name, + !unused, lockres->state, + lockres->inflight_assert_workers);list_move_tail (&dlm->purge_list, &lockres->purge);spin_unlock (&lockres->spinlock);continue ;
_
PATCH 4/8 ocfs2/dlm: do not purge lockres that is queued for assert master
Quoted 80 lines · Andrew Morton
fs/ocfs2/dlm/dlmmaster.c renamed from fs/ocfs2/dlm/dlmmaster.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +41
-1@@ -683,6 +684,43 @@ void dlm_lockres_drop_inflight_ref(strucwake_up (&res->wq); } +void __dlm_lockres_grab_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +assert_spin_locked (&res->spinlock); + res->inflight_assert_workers++; +mlog (0 ,"%s:%.*s: inflight assert worker++: now %u\n" , + dlm->name, res->lockname.len, res->lockname.name, + res->inflight_assert_workers); +} + +void dlm_lockres_grab_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +spin_lock (&res->spinlock); +__dlm_lockres_grab_inflight_worker (dlm, res); +spin_unlock (&res->spinlock); +} + +void __dlm_lockres_drop_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +assert_spin_locked (&res->spinlock); +BUG_ON (res->inflight_assert_workers ==0 ); + res->inflight_assert_workers--; +mlog (0 ,"%s:%.*s: inflight assert worker--: now %u\n" , + dlm->name, res->lockname.len, res->lockname.name, + res->inflight_assert_workers); +} + +void dlm_lockres_drop_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +spin_lock (&res->spinlock); +__dlm_lockres_drop_inflight_worker (dlm, res); +spin_unlock (&res->spinlock); +} +/* * lookup a lock resource by name. * may already exist in the hashtable. @@ -1603,7 +1641,8 @@ send_response:mlog (ML_ERROR ,"failed to dispatch assert master work\n" ); response =DLM_MASTER_RESP_ERROR ;dlm_lockres_put (res); - } + }else +dlm_lockres_grab_inflight_worker (dlm, res); }else {if (res)dlm_lockres_put (res);@@ -2118,6 +2157,8 @@ static void dlm_assert_master_worker(strdlm_lockres_release_ast (dlm, res); put: +dlm_lockres_drop_inflight_worker (dlm, res); +dlm_lockres_put (res);mlog (0 ,"finished with dlm_assert_master_worker\n" );diff -puN
fs/ocfs2/dlm/dlmrecovery.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmrecovery.cfs/ocfs2/dlm/dlmrecovery.c renamed from fs/ocfs2/dlm/dlmrecovery.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1@@ -1708,7 +1708,8 @@ int dlm_master_requery_handler(struct o2mlog_errno (-ENOMEM );/* retry!? */ BUG (); - } + }else +__dlm_lockres_grab_inflight_worker (dlm, res); }else /* put.. incase we are not the master */ dlm_lockres_put (res);spin_unlock (&res->spinlock);diff -puN
fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmthread.c
fs/ocfs2/dlm/dlmthread.c static void dlm_run_purge_list(struct dl · line 263
Quoted 6 lines
fs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1@@ -259,11 +259,14 @@ static void dlm_run_purge_list(struct dl* refs on it. */ unused =__dlm_lockres_unused (lockres);if (!unused || - (lockres->state &DLM_LOCK_RES_MIGRATING )) { + (lockres->state &DLM_LOCK_RES_MIGRATING ) || + (lockres->inflight_assert_workers !=0 )) {
If there's any assert master message we will halt purging *all* lock resources. That seems extreme to me :/
What about putting a flag on lockres->state to indicate that it's got some work queued? We would set it before queuing the work, then clear it in the worker, or if we ever cancel the work.
--Mark
--
Mark Fasheh
PATCH 4/8 ocfs2/dlm: do not purge lockres that is queued for assert master
Quoted 82 lines · Mark Fasheh
Quoted 81 lines · Andrew Morton
fs/ocfs2/dlm/dlmmaster.c renamed from fs/ocfs2/dlm/dlmmaster.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +41
-1@@ -683,6 +684,43 @@ void dlm_lockres_drop_inflight_ref(strucwake_up (&res->wq); } +void __dlm_lockres_grab_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +assert_spin_locked (&res->spinlock); + res->inflight_assert_workers++; +mlog (0 ,"%s:%.*s: inflight assert worker++: now %u\n" , + dlm->name, res->lockname.len, res->lockname.name, + res->inflight_assert_workers); +} + +void dlm_lockres_grab_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +spin_lock (&res->spinlock); +__dlm_lockres_grab_inflight_worker (dlm, res); +spin_unlock (&res->spinlock); +} + +void __dlm_lockres_drop_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +assert_spin_locked (&res->spinlock); +BUG_ON (res->inflight_assert_workers ==0 ); + res->inflight_assert_workers--; +mlog (0 ,"%s:%.*s: inflight assert worker--: now %u\n" , + dlm->name, res->lockname.len, res->lockname.name, + res->inflight_assert_workers); +} + +void dlm_lockres_drop_inflight_worker (struct dlm_ctxt *dlm, +struct dlm_lock_resource *res) +{ +spin_lock (&res->spinlock); +__dlm_lockres_drop_inflight_worker (dlm, res); +spin_unlock (&res->spinlock); +} +/* * lookup a lock resource by name. * may already exist in the hashtable. @@ -1603,7 +1641,8 @@ send_response:mlog (ML_ERROR ,"failed to dispatch assert master work\n" ); response =DLM_MASTER_RESP_ERROR ;dlm_lockres_put (res); - } + }else +dlm_lockres_grab_inflight_worker (dlm, res); }else {if (res)dlm_lockres_put (res);@@ -2118,6 +2157,8 @@ static void dlm_assert_master_worker(strdlm_lockres_release_ast (dlm, res); put: +dlm_lockres_drop_inflight_worker (dlm, res); +dlm_lockres_put (res);mlog (0 ,"finished with dlm_assert_master_worker\n" );diff -puN
fs/ocfs2/dlm/dlmrecovery.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmrecovery.cfs/ocfs2/dlm/dlmrecovery.c renamed from fs/ocfs2/dlm/dlmrecovery.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1@@ -1708,7 +1708,8 @@ int dlm_master_requery_handler(struct o2mlog_errno (-ENOMEM );/* retry!? */ BUG (); - } + }else +__dlm_lockres_grab_inflight_worker (dlm, res); }else /* put.. incase we are not the master */ dlm_lockres_put (res);spin_unlock (&res->spinlock);diff -puN
fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmthread.cfs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +0
-0@@ -259,11 +259,14 @@ static void dlm_run_purge_list(struct dl* refs on it. */
Quoted 9 lines
fs/ocfs2/dlm/dlmthread.c static void dlm_run_purge_list(struct dl · line 263
Quoted 5 lines
fs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1unused =__dlm_lockres_unused (lockres);if (!unused || - (lockres->state &DLM_LOCK_RES_MIGRATING )) { + (lockres->state &DLM_LOCK_RES_MIGRATING ) || + (lockres->inflight_assert_workers !=0 )) {If there's any assert master message we will halt purging *all* lock resources. That seems extreme to me :/
Not halt purging *all* lock resource, when one lockres is queued for master assert, it will be moved to the tail of the purge list, so dlm_thread can keep purging other lock resources.
-- joyce.xue
Quoted 9 lines
What about putting a flag on
lockres->stateto indicate that it's got some work queued? We would set it before queuing the work, then clear it in the worker, or if we ever cancel the work.--Mark--
Mark Fasheh
.
PATCH 4/8 ocfs2/dlm: do not purge lockres that is queued for assert master
Quoted 15 lines · On Mon, 16 Jun 2014 09:26:53 +0800 Xue jiufei <[email protected]> wrote:
Quoted 6 lines
Quoted 6 lines
spin_unlock(&res->spinlock);
diff -puNfs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmthread.cfs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +0
-0@@ -259,11 +259,14 @@ static void dlm_run_purge_list(struct dl* refs on it. */ Quoted 9 lines
fs/ocfs2/dlm/dlmthread.c static void dlm_run_purge_list(struct dl · line 263
Quoted 5 lines
fs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1unused =__dlm_lockres_unused (lockres);if (!unused || - (lockres->state &DLM_LOCK_RES_MIGRATING )) { + (lockres->state &DLM_LOCK_RES_MIGRATING ) || + (lockres->inflight_assert_workers !=0 )) {If there's any assert master message we will halt purging *all* lock resources. That seems extreme to me :/
Quoted 3 lines
Not halt purging *all* lock resource, when one lockres is queued for master assert, it will be moved to the tail of the purge list, so
dlm_threadcan keep purging other lock resources.
Where are we up to with this one?
Thanks.
PATCH 4/8 ocfs2/dlm: do not purge lockres that is queued for assert master
Quoted 17 lines · Andrew Morton
Quoted 15 lines · On Mon, 16 Jun 2014 09:26:53 +0800 Xue jiufei <[email protected]> wrote:
Quoted 15 lines
fs/ocfs2/dlm/dlmthread.c static void dlm_run_purge_list(struct dl · line 263
Quoted 11 lines
spin_unlock(&res->spinlock);
diff -puNfs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-masterfs/ocfs2/dlm/dlmthread.cfs/ocfs2/dlm/dlmthread.c renamed from fs/ocfs2/dlm/dlmthread.c~ocfs2-dlm-do-not-purge-lockres-that-is-queued-for-assert-master +2
-1@@ -259,11 +259,14 @@ static void dlm_run_purge_list(struct dl* refs on it. */ unused =__dlm_lockres_unused (lockres);if (!unused || - (lockres->state &DLM_LOCK_RES_MIGRATING )) { + (lockres->state &DLM_LOCK_RES_MIGRATING ) || + (lockres->inflight_assert_workers !=0 )) {If there's any assert master message we will halt purging *all* lock resources. That seems extreme to me :/
Quoted 5 lines
Quoted 3 lines
Not halt purging *all* lock resource, when one lockres is queued for master assert, it will be moved to the tail of the purge list, so
dlm_threadcan keep purging other lock resources.Where are we up to with this one?
I read the code wrong, Xue is correct:
Thanks,
--Mark
--
Mark Fasheh