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 | ||
