ocfs2: fix deadlock between o2hb thread and o2net_wq
authorJoseph Qi <joseph.qi@huawei.com>
Thu, 9 Oct 2014 22:25:13 +0000 (15:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 10 Oct 2014 02:25:47 +0000 (22:25 -0400)
The following case may lead to o2net_wq and o2hb thread deadlock on
o2hb_callback_sem.
Currently there are 2 nodes say N1, N2 in the cluster. And N2 down, at
the same time, N3 tries to join the cluster. So N1 will handle node
down (N2) and join (N3) simultaneously.
    o2hb                               o2net_wq
    ->o2hb_do_disk_heartbeat
    ->o2hb_check_slot
    ->o2hb_run_event_list
    ->o2hb_fire_callbacks
    ->down_write(&o2hb_callback_sem)
    ->o2net_hb_node_down_cb
    ->flush_workqueue(o2net_wq)
                                       ->o2net_process_message
                                       ->dlm_query_join_handler
                                       ->o2hb_check_node_heartbeating
                                       ->o2hb_fill_node_map
                                       ->down_read(&o2hb_callback_sem)

No need to take o2hb_callback_sem in dlm_query_join_handler,
o2hb_live_lock is enough to protect live node map.

Signed-off-by: Joseph Qi <joseph.qi@huawei.com>
Cc: xMark Fasheh <mfasheh@suse.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: jiangyiwen <jiangyiwen@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/ocfs2/cluster/heartbeat.c
fs/ocfs2/cluster/heartbeat.h
fs/ocfs2/dlm/dlmdomain.c

index 73039295d0d1f35205e060220521934afd33ff27..d1338544816896dc773fc930b43dc44bcc157266 100644 (file)
@@ -2572,6 +2572,25 @@ int o2hb_check_node_heartbeating(u8 node_num)
 }
 EXPORT_SYMBOL_GPL(o2hb_check_node_heartbeating);
 
+int o2hb_check_node_heartbeating_no_sem(u8 node_num)
+{
+       unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
+       unsigned long flags;
+
+       spin_lock_irqsave(&o2hb_live_lock, flags);
+       o2hb_fill_node_map_from_callback(testing_map, sizeof(testing_map));
+       spin_unlock_irqrestore(&o2hb_live_lock, flags);
+       if (!test_bit(node_num, testing_map)) {
+               mlog(ML_HEARTBEAT,
+                    "node (%u) does not have heartbeating enabled.\n",
+                    node_num);
+               return 0;
+       }
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(o2hb_check_node_heartbeating_no_sem);
+
 int o2hb_check_node_heartbeating_from_callback(u8 node_num)
 {
        unsigned long testing_map[BITS_TO_LONGS(O2NM_MAX_NODES)];
index 00ad8e8fea510ede2cf33d48eecf9178d65a9605..3ef5137dc362200701a6fbd4c93f9724a4594ec2 100644 (file)
@@ -80,6 +80,7 @@ void o2hb_fill_node_map(unsigned long *map,
 void o2hb_exit(void);
 int o2hb_init(void);
 int o2hb_check_node_heartbeating(u8 node_num);
+int o2hb_check_node_heartbeating_no_sem(u8 node_num);
 int o2hb_check_node_heartbeating_from_callback(u8 node_num);
 int o2hb_check_local_node_heartbeating(void);
 void o2hb_stop_all_regions(void);
index 257a6dfe3f13da9e3fded7454f6b38cdccd414e9..02d315fef432a73c5f9af130c37dd2b0c60b3852 100644 (file)
@@ -839,7 +839,7 @@ static int dlm_query_join_handler(struct o2net_msg *msg, u32 len, void *data,
         * to back off and try again.  This gives heartbeat a chance
         * to catch up.
         */
-       if (!o2hb_check_node_heartbeating(query->node_idx)) {
+       if (!o2hb_check_node_heartbeating_no_sem(query->node_idx)) {
                mlog(0, "node %u is not in our live map yet\n",
                     query->node_idx);