diff options
author | Faisal Latif <faisal.latif@intel.com> | 2008-11-21 21:50:41 -0500 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2008-12-05 14:00:02 -0500 |
commit | 879e5bd5a1a0a317fb67fa4dc550db092a7bdcb0 (patch) | |
tree | 220e3f3d077f123032d13253aa6cc8570b549917 | |
parent | c5d321e5c924384cf5b35f6288d69e9237490565 (diff) |
RDMA/nes: Lock down connected_nodes list while processing it
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>
-rw-r--r-- | drivers/infiniband/hw/nes/nes_cm.c | 40 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes_cm.h | 2 |
2 files changed, 30 insertions, 12 deletions
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 2a1d6c7f8d32..257d994ec7b5 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c | |||
@@ -459,13 +459,23 @@ static void nes_cm_timer_tick(unsigned long pass) | |||
459 | int ret = NETDEV_TX_OK; | 459 | int ret = NETDEV_TX_OK; |
460 | enum nes_cm_node_state last_state; | 460 | enum nes_cm_node_state last_state; |
461 | 461 | ||
462 | struct list_head timer_list; | ||
463 | INIT_LIST_HEAD(&timer_list); | ||
462 | spin_lock_irqsave(&cm_core->ht_lock, flags); | 464 | spin_lock_irqsave(&cm_core->ht_lock, flags); |
463 | 465 | ||
464 | list_for_each_safe(list_node, list_core_temp, | 466 | list_for_each_safe(list_node, list_core_temp, |
465 | &cm_core->connected_nodes) { | 467 | &cm_core->connected_nodes) { |
466 | cm_node = container_of(list_node, struct nes_cm_node, list); | 468 | cm_node = container_of(list_node, struct nes_cm_node, list); |
467 | add_ref_cm_node(cm_node); | 469 | if (!list_empty(&cm_node->recv_list) || (cm_node->send_entry)) { |
468 | spin_unlock_irqrestore(&cm_core->ht_lock, flags); | 470 | add_ref_cm_node(cm_node); |
471 | list_add(&cm_node->timer_entry, &timer_list); | ||
472 | } | ||
473 | } | ||
474 | spin_unlock_irqrestore(&cm_core->ht_lock, flags); | ||
475 | |||
476 | list_for_each_safe(list_node, list_core_temp, &timer_list) { | ||
477 | cm_node = container_of(list_node, struct nes_cm_node, | ||
478 | timer_entry); | ||
469 | spin_lock_irqsave(&cm_node->recv_list_lock, flags); | 479 | spin_lock_irqsave(&cm_node->recv_list_lock, flags); |
470 | list_for_each_safe(list_core, list_node_temp, | 480 | list_for_each_safe(list_core, list_node_temp, |
471 | &cm_node->recv_list) { | 481 | &cm_node->recv_list) { |
@@ -615,14 +625,12 @@ static void nes_cm_timer_tick(unsigned long pass) | |||
615 | 625 | ||
616 | spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); | 626 | spin_unlock_irqrestore(&cm_node->retrans_list_lock, flags); |
617 | rem_ref_cm_node(cm_node->cm_core, cm_node); | 627 | rem_ref_cm_node(cm_node->cm_core, cm_node); |
618 | spin_lock_irqsave(&cm_core->ht_lock, flags); | ||
619 | if (ret != NETDEV_TX_OK) { | 628 | if (ret != NETDEV_TX_OK) { |
620 | nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n", | 629 | nes_debug(NES_DBG_CM, "rexmit failed for cm_node=%p\n", |
621 | cm_node); | 630 | cm_node); |
622 | break; | 631 | break; |
623 | } | 632 | } |
624 | } | 633 | } |
625 | spin_unlock_irqrestore(&cm_core->ht_lock, flags); | ||
626 | 634 | ||
627 | if (settimer) { | 635 | if (settimer) { |
628 | if (!timer_pending(&cm_core->tcp_timer)) { | 636 | if (!timer_pending(&cm_core->tcp_timer)) { |
@@ -925,28 +933,36 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core, | |||
925 | struct list_head *list_pos = NULL; | 933 | struct list_head *list_pos = NULL; |
926 | struct list_head *list_temp = NULL; | 934 | struct list_head *list_temp = NULL; |
927 | struct nes_cm_node *cm_node = NULL; | 935 | struct nes_cm_node *cm_node = NULL; |
936 | struct list_head reset_list; | ||
928 | 937 | ||
929 | nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, " | 938 | nes_debug(NES_DBG_CM, "attempting listener= %p free_nodes= %d, " |
930 | "refcnt=%d\n", listener, free_hanging_nodes, | 939 | "refcnt=%d\n", listener, free_hanging_nodes, |
931 | atomic_read(&listener->ref_count)); | 940 | atomic_read(&listener->ref_count)); |
932 | /* free non-accelerated child nodes for this listener */ | 941 | /* free non-accelerated child nodes for this listener */ |
942 | INIT_LIST_HEAD(&reset_list); | ||
933 | if (free_hanging_nodes) { | 943 | if (free_hanging_nodes) { |
934 | spin_lock_irqsave(&cm_core->ht_lock, flags); | 944 | spin_lock_irqsave(&cm_core->ht_lock, flags); |
935 | list_for_each_safe(list_pos, list_temp, | 945 | list_for_each_safe(list_pos, list_temp, |
936 | &g_cm_core->connected_nodes) { | 946 | &g_cm_core->connected_nodes) { |
937 | cm_node = container_of(list_pos, struct nes_cm_node, | 947 | cm_node = container_of(list_pos, struct nes_cm_node, |
938 | list); | 948 | list); |
939 | if ((cm_node->listener == listener) && | 949 | if ((cm_node->listener == listener) && |
940 | (!cm_node->accelerated)) { | 950 | (!cm_node->accelerated)) { |
941 | cleanup_retrans_entry(cm_node); | 951 | add_ref_cm_node(cm_node); |
942 | spin_unlock_irqrestore(&cm_core->ht_lock, | 952 | list_add(&cm_node->reset_entry, &reset_list); |
943 | flags); | ||
944 | send_reset(cm_node, NULL); | ||
945 | spin_lock_irqsave(&cm_core->ht_lock, flags); | ||
946 | } | 953 | } |
947 | } | 954 | } |
948 | spin_unlock_irqrestore(&cm_core->ht_lock, flags); | 955 | spin_unlock_irqrestore(&cm_core->ht_lock, flags); |
949 | } | 956 | } |
957 | |||
958 | list_for_each_safe(list_pos, list_temp, &reset_list) { | ||
959 | cm_node = container_of(list_pos, struct nes_cm_node, | ||
960 | reset_entry); | ||
961 | cleanup_retrans_entry(cm_node); | ||
962 | send_reset(cm_node, NULL); | ||
963 | rem_ref_cm_node(cm_node->cm_core, cm_node); | ||
964 | } | ||
965 | |||
950 | spin_lock_irqsave(&cm_core->listen_list_lock, flags); | 966 | spin_lock_irqsave(&cm_core->listen_list_lock, flags); |
951 | if (!atomic_dec_return(&listener->ref_count)) { | 967 | if (!atomic_dec_return(&listener->ref_count)) { |
952 | list_del(&listener->list); | 968 | list_del(&listener->list); |
diff --git a/drivers/infiniband/hw/nes/nes_cm.h b/drivers/infiniband/hw/nes/nes_cm.h index 367b3d290140..282a9cbe508f 100644 --- a/drivers/infiniband/hw/nes/nes_cm.h +++ b/drivers/infiniband/hw/nes/nes_cm.h | |||
@@ -292,6 +292,8 @@ struct nes_cm_node { | |||
292 | int apbvt_set; | 292 | int apbvt_set; |
293 | int accept_pend; | 293 | int accept_pend; |
294 | int freed; | 294 | int freed; |
295 | struct list_head timer_entry; | ||
296 | struct list_head reset_entry; | ||
295 | struct nes_qp *nesqp; | 297 | struct nes_qp *nesqp; |
296 | }; | 298 | }; |
297 | 299 | ||