ocfs2: o2dlm: fix a race between purge and master query
authorSrinivas Eeda <srinivas.eeda@oracle.com>
Wed, 10 Dec 2014 23:41:48 +0000 (15:41 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Dec 2014 01:41:03 +0000 (17:41 -0800)
Node A sends master query request to node B which is the master.  At this
time lockres happens to be on purgelist.  dlm_master_request_handler gets
the dlm spinlock, finds the resource and releases the dlm spin lock.
Right at this dlm_thread on this node could purge the lockres.
dlm_master_request_handler can then acquire lockres spinlock and reply to
Node A that node B is the master even though lockres on node B is purged.

The above scenario will now make node A falsely think node B is the master
which is inconsistent.  Further if another node C tries to master the same
resource, every node will respond they are not the master.  Node C then
masters the resource and sends assert master to all nodes.  This will now
make node A crash with the following message.

dlm_assert_master_handler:1831 ERROR: DIE! Mastery assert from 9, but current
owner is 10!

Signed-off-by: Srinivas Eeda <srinivas.eeda@oracle.com>
Cc: Mark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Reviewed-by: Wengang Wang <wen.gang.wang@oracle.com>
Tested-by: Joseph Qi <joseph.qi@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/ocfs2/dlm/dlmmaster.c

index 215e41abf101a1e70afbe2b151980ed237a632a0..3689b35920422304b47fa92f6c65ced6a9b316c4 100644 (file)
@@ -1460,6 +1460,18 @@ way_up_top:
 
                /* take care of the easy cases up front */
                spin_lock(&res->spinlock);
+
+               /*
+                * Right after dlm spinlock was released, dlm_thread could have
+                * purged the lockres. Check if lockres got unhashed. If so
+                * start over.
+                */
+               if (hlist_unhashed(&res->hash_node)) {
+                       spin_unlock(&res->spinlock);
+                       dlm_lockres_put(res);
+                       goto way_up_top;
+               }
+
                if (res->state & (DLM_LOCK_RES_RECOVERING|
                                  DLM_LOCK_RES_MIGRATING)) {
                        spin_unlock(&res->spinlock);