ocfs2/dlm: fix a race between purge and migration
PATCH v2 1/1 ocfs2/dlm: fix a race between purge and migration
We found a race between purge and migration when doing code review. Node A put lockres to purgelist before receiving the migrate message from node B which is the master. Node A call dlm_mig_lockres_handler to handle this message.
dlm_mig_lockres_handler
dlm_lookup_lockres
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
race window,
dlm_run_purge_listmay run and send
deref message to master, waiting the response
spin_lock(&res->spinlock);
res->state |= DLM_LOCK_RES_MIGRATING; spin_unlock(&res->spinlock); dlm_mig_lockres_handler returns
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
dlm_threadreceives the response from master for the deref
message and triggers the BUG because the lockres has the state DLM_LOCK_RES_MIGRATING with the following message:
dlm_purge_lockres:209 ERROR: 6633EB681FA7474A9C280A4E1A836F0F:
res M0000000000000000030c0300000000 in use after deref
--- fs/ocfs2/dlm/dlmrecovery.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/ocfs2/dlm/dlmrecovery.c +8 -1
@@ -1373,6 +1373,7 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,char *buf =NULL ;struct dlm_work_item *item =NULL ;struct dlm_lock_resource *res =NULL ; +unsigned int hash;if (!dlm_grab (dlm))return -EINVAL ;
@@ -1400,7 +1401,10 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,/* lookup the lock to see if we have a secondary queue for this * already... just add the locks in and this will have its owner * and RECOVERY flag changed when it completes. */ - res =dlm_lookup_lockres (dlm, mres->lockname, mres->lockname_len); + hash =dlm_lockid_hash (mres->lockname, mres->lockname_len); +spin_lock (&dlm->spinlock); + res =__dlm_lookup_lockres (dlm, mres->lockname, mres->lockname_len, + hash);if (res) {/* this will get a ref on res */ /* mark it as recovering/migrating and hash it */
@@ -1421,13 +1425,16 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data, mres->lockname_len, mres->lockname); ret = -EFAULT ;spin_unlock (&res->spinlock); +spin_unlock (&dlm->spinlock);dlm_lockres_put (res); goto leave; } res->state |=DLM_LOCK_RES_MIGRATING ; }spin_unlock (&res->spinlock); +spin_unlock (&dlm->spinlock); }else { +spin_unlock (&dlm->spinlock);/* need to allocate, just like if it was * mastered here normally */ res =dlm_new_lockres (dlm, mres->lockname, mres->lockname_len);
Signature · 1 line
1.8.4.3
PATCH v2 1/1 ocfs2/dlm: fix a race between purge and migration
Quoted 15 lines · Xue jiufei
We found a race between purge and migration when doing code review. Node A put lockres to purgelist before receiving the migrate message from node B which is the master. Node A call
dlm_mig_lockres_handlerto handle this message.
dlm_mig_lockres_handler
dlm_lookup_lockresQuoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
race window,
dlm_run_purge_listmay run and sendderef message to master, waiting the response
spin_lock(&res->spinlock);
res->state|=DLM_LOCK_RES_MIGRATING;spin_unlock(&res->spinlock);dlm_mig_lockres_handlerreturnsQuoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
dlm_threadreceives the response from master for the deref
Quoted 8 lines
message and triggers the BUG because the lockres has the state
DLM_LOCK_RES_MIGRATINGwith the following message:dlm_purge_lockres:209 ERROR: 6633EB681FA7474A9C280A4E1A836F0F:
res M0000000000000000030c0300000000 in use after derefSigned-off-by Jiufei Xue <[email protected]>Reviewed-by Joseph Qi <[email protected]>
Looks good.
Quoted 46 lines
--- fs/ocfs2/dlm/dlmrecovery.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)fs/ocfs2/dlm/dlmrecovery.c +8
-1@@ -1373,6 +1373,7 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,char *buf =NULL ;struct dlm_work_item *item =NULL ;struct dlm_lock_resource *res =NULL ; +unsigned int hash;if (!dlm_grab (dlm))return -EINVAL ;@@ -1400,7 +1401,10 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,/* lookup the lock to see if we have a secondary queue for this * already... just add the locks in and this will have its owner * and RECOVERY flag changed when it completes. */ - res =dlm_lookup_lockres (dlm, mres->lockname, mres->lockname_len); + hash =dlm_lockid_hash (mres->lockname, mres->lockname_len); +spin_lock (&dlm->spinlock); + res =__dlm_lookup_lockres (dlm, mres->lockname, mres->lockname_len, + hash);if (res) {/* this will get a ref on res */ /* mark it as recovering/migrating and hash it */ @@ -1421,13 +1425,16 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data, mres->lockname_len, mres->lockname); ret = -EFAULT ;spin_unlock (&res->spinlock); +spin_unlock (&dlm->spinlock);dlm_lockres_put (res); goto leave; } res->state |=DLM_LOCK_RES_MIGRATING ; }spin_unlock (&res->spinlock); +spin_unlock (&dlm->spinlock); }else { +spin_unlock (&dlm->spinlock);/* need to allocate, just like if it was * mastered here normally */ res =dlm_new_lockres (dlm, mres->lockname, mres->lockname_len);