aboutsummaryrefslogtreecommitdiff
path: root/drivers/infiniband/hw/nes/nes_cm.c
diff options
context:
space:
mode:
authorFaisal Latif <faisal.latif@intel.com>2008-11-21 20:50:41 -0600
committerRoland Dreier <rolandd@cisco.com>2008-12-05 11:00:02 -0800
commit879e5bd5a1a0a317fb67fa4dc550db092a7bdcb0 (patch)
tree220e3f3d077f123032d13253aa6cc8570b549917 /drivers/infiniband/hw/nes/nes_cm.c
parentc5d321e5c924384cf5b35f6288d69e9237490565 (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>
Diffstat (limited to 'drivers/infiniband/hw/nes/nes_cm.c')
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c40
1 files changed, 28 insertions, 12 deletions
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 2a1d6c7f8d3..257d994ec7b 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)
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);