RDMA/nes: Lock down connected_nodes list while processing it
authorFaisal Latif <faisal.latif@intel.com>
Sat, 22 Nov 2008 02:50:41 +0000 (20:50 -0600)
committerRoland Dreier <rolandd@cisco.com>
Fri, 5 Dec 2008 19:00:02 +0000 (11:00 -0800)
While processing connected_nodes list, we would release the lock when
we need to send reset to remote partner.  That created a window where
the list can be modified.  Change this into a two step process: place
nodes that need processing on a local list then process the local list.

Signed-off-by: Faisal Latif <faisal.latif@intel.com>
Signed-off-by: Chien Tung <chien.tin.tung@intel.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/nes/nes_cm.c
drivers/infiniband/hw/nes/nes_cm.h

index 2a1d6c7f8d32970ca486a9f2a6792df011183235..257d994ec7b5ef57422fe0b07a947bdee4e19291 100644 (file)
@@ -459,13 +459,23 @@ static void nes_cm_timer_tick(unsigned long pass)
        int ret = NETDEV_TX_OK;
        enum nes_cm_node_state last_state;
 
+       struct list_head timer_list;
+       INIT_LIST_HEAD(&timer_list);
        spin_lock_irqsave(&cm_core->ht_lock, flags);
 
        list_for_each_safe(list_node, list_core_temp,
-               &cm_core->connected_nodes) {
+                               &cm_core->connected_nodes) {
                cm_node = container_of(list_node, struct nes_cm_node, list);
-               add_ref_cm_node(cm_node);
-               spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+               if (!list_empty(&cm_node->recv_list) || (cm_node->send_entry)) {
+                       add_ref_cm_node(cm_node);
+                       list_add(&cm_node->timer_entry, &timer_list);
+               }
+       }
+       spin_unlock_irqrestore(&cm_core->ht_lock, flags);
+
+       list_for_each_safe(list_node, list_core_temp, &timer_list) {
+               cm_node = container_of(list_node, struct nes_cm_node,
+                                       timer_entry);
                spin_lock_irqsave(&cm_node->recv_list_lock, flags);
                list_for_each_safe(list_core, list_node_temp,
                        &cm_node->recv_list) {
@@ -615,14 +625,12 @@ static void nes_cm_timer_tick(unsigned long pass)
 
                spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags);
                rem_ref_cm_node(cm_node->cm_core, cm_node);
-               spin_lock_irqsave(&cm_core->ht_lock, flags);
                if (ret != NETDEV_TX_OK) {
                        nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n",
                                cm_node);
                        break;
                }
        }
-       spin_unlock_irqrestore(&cm_core->ht_lock, flags);
 
        if (settimer) {
                if (!timer_pending(&cm_core->tcp_timer)) {
@@ -925,28 +933,36 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
        struct list_head *list_pos = NULL;
        struct list_head *list_temp = NULL;
        struct nes_cm_node *cm_node = NULL;
+       struct list_head reset_list;
 
        nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, "
                "refcnt=%d\n", listener, free_hanging_nodes,
                atomic_read(&listener->ref_count));
        /* free non-accelerated child nodes for this listener */
+       INIT_LIST_HEAD(&reset_list);
        if (free_hanging_nodes) {
                spin_lock_irqsave(&cm_core->ht_lock, flags);
                list_for_each_safe(list_pos, list_temp,
-                       &g_cm_core->connected_nodes) {
+                                  &g_cm_core->connected_nodes) {
                        cm_node = container_of(list_pos, struct nes_cm_node,
                                list);
                        if ((cm_node->listener == listener) &&
-                               (!cm_node->accelerated)) {
-                               cleanup_retrans_entry(cm_node);
-                               spin_unlock_irqrestore(&cm_core->ht_lock,
-                                       flags);
-                               send_reset(cm_node, NULL);
-                               spin_lock_irqsave(&cm_core->ht_lock, flags);
+                           (!cm_node->accelerated)) {
+                               add_ref_cm_node(cm_node);
+                               list_add(&cm_node->reset_entry, &reset_list);
                        }
                }
                spin_unlock_irqrestore(&cm_core->ht_lock, flags);
        }
+
+       list_for_each_safe(list_pos, list_temp, &reset_list) {
+               cm_node = container_of(list_pos, struct nes_cm_node,
+                                       reset_entry);
+               cleanup_retrans_entry(cm_node);
+               send_reset(cm_node, NULL);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
+       }
+
        spin_lock_irqsave(&cm_core->listen_list_lock, flags);
        if (!atomic_dec_return(&listener->ref_count)) {
                list_del(&listener->list);
index 367b3d29014074252424c798f110821b5ce0c7c4..282a9cbe508fc70732c009de40234577684958eb 100644 (file)
@@ -292,6 +292,8 @@ struct nes_cm_node {
        int                       apbvt_set;
        int                       accept_pend;
        int                     freed;
+       struct list_head        timer_entry;
+       struct list_head        reset_entry;
        struct nes_qp           *nesqp;
 };