include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / drivers / infiniband / hw / nes / nes_cm.c
index 7da5437..986d6f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 - 2009 Intel-NE, Inc.  All rights reserved.
+ * Copyright (c) 2006 - 2009 Intel Corporation.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
 #include <linux/random.h>
 #include <linux/list.h>
 #include <linux/threads.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
 #include <net/arp.h>
 #include <net/neighbour.h>
 #include <net/route.h>
 #include <net/ip_fib.h>
+#include <net/tcp.h>
 
 #include "nes.h"
 
@@ -65,8 +68,8 @@ u32 cm_packets_dropped;
 u32 cm_packets_retrans;
 u32 cm_packets_created;
 u32 cm_packets_received;
-u32 cm_listens_created;
-u32 cm_listens_destroyed;
+atomic_t cm_listens_created;
+atomic_t cm_listens_destroyed;
 u32 cm_backlog_drops;
 atomic_t cm_loopbacks;
 atomic_t cm_nodes_created;
@@ -250,6 +253,33 @@ static int parse_mpa(struct nes_cm_node *cm_node, u8 *buffer, u32 *type,
 
        mpa_frame = (struct ietf_mpa_frame *)buffer;
        cm_node->mpa_frame_size = ntohs(mpa_frame->priv_data_len);
+       /* make sure mpa private data len is less than 512 bytes */
+       if (cm_node->mpa_frame_size > IETF_MAX_PRIV_DATA_LEN) {
+               nes_debug(NES_DBG_CM, "The received Length of Private"
+                       " Data field exceeds 512 octets\n");
+               return -EINVAL;
+       }
+       /*
+        * make sure MPA receiver interoperate with the
+        * received MPA version and MPA key information
+        *
+        */
+       if (mpa_frame->rev != mpa_version) {
+               nes_debug(NES_DBG_CM, "The received mpa version"
+                               " can not be interoperated\n");
+               return -EINVAL;
+       }
+       if (cm_node->state != NES_CM_STATE_MPAREQ_SENT) {
+               if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE)) {
+                       nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
+                       return -EINVAL;
+               }
+       } else {
+               if (memcmp(mpa_frame->key, IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE)) {
+                       nes_debug(NES_DBG_CM, "Unexpected MPA Key received \n");
+                       return -EINVAL;
+               }
+       }
 
        if (cm_node->mpa_frame_size + sizeof(struct ietf_mpa_frame) != len) {
                nes_debug(NES_DBG_CM, "The received ietf buffer was not right"
@@ -471,6 +501,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb,
 
 static void nes_retrans_expired(struct nes_cm_node *cm_node)
 {
+       struct iw_cm_id *cm_id = cm_node->cm_id;
        switch (cm_node->state) {
        case NES_CM_STATE_SYN_RCVD:
        case NES_CM_STATE_CLOSING:
@@ -478,10 +509,14 @@ static void nes_retrans_expired(struct nes_cm_node *cm_node)
                break;
        case NES_CM_STATE_LAST_ACK:
        case NES_CM_STATE_FIN_WAIT1:
-       case NES_CM_STATE_MPAREJ_RCVD:
+               if (cm_node->cm_id)
+                       cm_id->rem_ref(cm_id);
+               cm_node->state = NES_CM_STATE_CLOSED;
                send_reset(cm_node, NULL);
                break;
        default:
+               add_ref_cm_node(cm_node);
+               send_reset(cm_node, NULL);
                create_event(cm_node, NES_CM_EVENT_ABORTED);
        }
 }
@@ -540,6 +575,7 @@ static void nes_cm_timer_tick(unsigned long pass)
        struct list_head *list_node;
        struct nes_cm_core *cm_core = g_cm_core;
        u32 settimer = 0;
+       unsigned long timetosend;
        int ret = NETDEV_TX_OK;
 
        struct list_head timer_list;
@@ -644,8 +680,11 @@ static void nes_cm_timer_tick(unsigned long pass)
                                send_entry->retrycount);
                        if (send_entry->send_retrans) {
                                send_entry->retranscount--;
+                               timetosend = (NES_RETRY_TIMEOUT <<
+                                       (NES_DEFAULT_RETRANS - send_entry->retranscount));
+
                                send_entry->timetosend = jiffies +
-                                       NES_RETRY_TIMEOUT;
+                                       min(timetosend, NES_MAX_TIMEOUT);
                                if (nexttimeout > send_entry->timetosend ||
                                        !settimer) {
                                        nexttimeout = send_entry->timetosend;
@@ -941,6 +980,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                reset_entry);
                {
                        struct nes_cm_node *loopback = cm_node->loopbackpartner;
+                       enum nes_cm_node_state old_state;
                        if (NES_CM_STATE_FIN_WAIT1 <= cm_node->state) {
                                rem_ref_cm_node(cm_node->cm_core, cm_node);
                        } else {
@@ -952,11 +992,12 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                                         NES_CM_STATE_CLOSED;
                                                WARN_ON(1);
                                        } else {
-                                               cm_node->state =
-                                                       NES_CM_STATE_CLOSED;
-                                               rem_ref_cm_node(
-                                                       cm_node->cm_core,
-                                                       cm_node);
+                                               old_state = cm_node->state;
+                                               cm_node->state = NES_CM_STATE_LISTENER_DESTROYED;
+                                               if (old_state != NES_CM_STATE_MPAREQ_RCVD)
+                                                       rem_ref_cm_node(
+                                                               cm_node->cm_core,
+                                                               cm_node);
                                        }
                                } else {
                                        struct nes_cm_event event;
@@ -971,20 +1012,10 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                                        event.cm_info.loc_port =
                                                         loopback->loc_port;
                                        event.cm_info.cm_id = loopback->cm_id;
-                                       cm_event_connect_error(&event);
+                                       add_ref_cm_node(loopback);
                                        loopback->state = NES_CM_STATE_CLOSED;
-
-                                       event.cm_node = cm_node;
-                                       event.cm_info.rem_addr =
-                                                        cm_node->rem_addr;
-                                       event.cm_info.loc_addr =
-                                                        cm_node->loc_addr;
-                                       event.cm_info.rem_port =
-                                                        cm_node->rem_port;
-                                       event.cm_info.loc_port =
-                                                        cm_node->loc_port;
-                                       event.cm_info.cm_id = cm_node->cm_id;
-                                       cm_event_reset(&event);
+                                       cm_event_connect_error(&event);
+                                       cm_node->state = NES_CM_STATE_LISTENER_DESTROYED;
 
                                        rem_ref_cm_node(cm_node->cm_core,
                                                         cm_node);
@@ -1013,7 +1044,7 @@ static int mini_cm_dec_refcnt_listen(struct nes_cm_core *cm_core,
                kfree(listener);
                listener = NULL;
                ret = 0;
-               cm_listens_destroyed++;
+               atomic_inc(&cm_listens_destroyed);
        } else {
                spin_unlock_irqrestore(&cm_core->listen_list_lock, flags);
        }
@@ -1069,12 +1100,13 @@ static inline int mini_cm_accelerated(struct nes_cm_core *cm_core,
 /**
  * nes_addr_resolve_neigh
  */
-static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
+static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpindex)
 {
        struct rtable *rt;
        struct flowi fl;
        struct neighbour *neigh;
-       int rc = -1;
+       int rc = arpindex;
+       struct nes_adapter *nesadapter = nesvnic->nesdev->nesadapter;
 
        memset(&fl, 0, sizeof fl);
        fl.nl_u.ip4_u.daddr = htonl(dst_ip);
@@ -1090,6 +1122,21 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
                        nes_debug(NES_DBG_CM, "Neighbor MAC address for 0x%08X"
                                  " is %pM, Gateway is 0x%08X \n", dst_ip,
                                  neigh->ha, ntohl(rt->rt_gateway));
+
+                       if (arpindex >= 0) {
+                               if (!memcmp(nesadapter->arp_table[arpindex].mac_addr,
+                                                       neigh->ha, ETH_ALEN)){
+                                       /* Mac address same as in nes_arp_table */
+                                       neigh_release(neigh);
+                                       ip_rt_put(rt);
+                                       return rc;
+                               }
+
+                               nes_manage_arp_cache(nesvnic->netdev,
+                                               nesadapter->arp_table[arpindex].mac_addr,
+                                               dst_ip, NES_ARP_DELETE);
+                       }
+
                        nes_manage_arp_cache(nesvnic->netdev, neigh->ha,
                                             dst_ip, NES_ARP_ADD);
                        rc = nes_arp_table(nesvnic->nesdev, dst_ip, NULL,
@@ -1105,7 +1152,6 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip)
        return rc;
 }
 
-
 /**
  * make_cm_node - create a new instance of a cm node
  */
@@ -1115,6 +1161,7 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
 {
        struct nes_cm_node *cm_node;
        struct timespec ts;
+       int oldarpindex = 0;
        int arpindex = 0;
        struct nes_device *nesdev;
        struct nes_adapter *nesadapter;
@@ -1168,17 +1215,18 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
        nesadapter = nesdev->nesadapter;
 
        cm_node->loopbackpartner = NULL;
+
        /* get the mac addr for the remote node */
        if (ipv4_is_loopback(htonl(cm_node->rem_addr)))
                arpindex = nes_arp_table(nesdev, ntohl(nesvnic->local_ipaddr), NULL, NES_ARP_RESOLVE);
-       else
-               arpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
+       else {
+               oldarpindex = nes_arp_table(nesdev, cm_node->rem_addr, NULL, NES_ARP_RESOLVE);
+               arpindex = nes_addr_resolve_neigh(nesvnic, cm_info->rem_addr, oldarpindex);
+
+       }
        if (arpindex < 0) {
-               arpindex = nes_addr_resolve_neigh(nesvnic, cm_info->rem_addr);
-               if (arpindex < 0) {
-                       kfree(cm_node);
-                       return NULL;
-               }
+               kfree(cm_node);
+               return NULL;
        }
 
        /* copy the mac addr to node context */
@@ -1321,18 +1369,27 @@ static void handle_fin_pkt(struct nes_cm_node *cm_node)
        nes_debug(NES_DBG_CM, "Received FIN, cm_node = %p, state = %u. "
                "refcnt=%d\n", cm_node, cm_node->state,
                atomic_read(&cm_node->ref_count));
-       cm_node->tcp_cntxt.rcv_nxt++;
-       cleanup_retrans_entry(cm_node);
        switch (cm_node->state) {
        case NES_CM_STATE_SYN_RCVD:
        case NES_CM_STATE_SYN_SENT:
        case NES_CM_STATE_ESTABLISHED:
-       case NES_CM_STATE_MPAREQ_SENT:
        case NES_CM_STATE_MPAREJ_RCVD:
+               cm_node->tcp_cntxt.rcv_nxt++;
+               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_LAST_ACK;
                send_fin(cm_node, NULL);
                break;
+       case NES_CM_STATE_MPAREQ_SENT:
+               create_event(cm_node, NES_CM_EVENT_ABORTED);
+               cm_node->tcp_cntxt.rcv_nxt++;
+               cleanup_retrans_entry(cm_node);
+               cm_node->state = NES_CM_STATE_CLOSED;
+               add_ref_cm_node(cm_node);
+               send_reset(cm_node, NULL);
+               break;
        case NES_CM_STATE_FIN_WAIT1:
+               cm_node->tcp_cntxt.rcv_nxt++;
+               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_CLOSING;
                send_ack(cm_node, NULL);
                /* Wait for ACK as this is simultanous close..
@@ -1340,11 +1397,15 @@ static void handle_fin_pkt(struct nes_cm_node *cm_node)
                * Just rm the node.. Done.. */
                break;
        case NES_CM_STATE_FIN_WAIT2:
+               cm_node->tcp_cntxt.rcv_nxt++;
+               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_TIME_WAIT;
                send_ack(cm_node, NULL);
                schedule_nes_timer(cm_node, NULL,  NES_TIMER_TYPE_CLOSE, 1, 0);
                break;
        case NES_CM_STATE_TIME_WAIT:
+               cm_node->tcp_cntxt.rcv_nxt++;
+               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_CLOSED;
                rem_ref_cm_node(cm_node->cm_core, cm_node);
                break;
@@ -1380,7 +1441,6 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
                passive_state = atomic_add_return(1, &cm_node->passive_state);
                if (passive_state ==  NES_SEND_RESET_EVENT)
                        create_event(cm_node, NES_CM_EVENT_RESET);
-               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_CLOSED;
                dev_kfree_skb_any(skb);
                break;
@@ -1394,18 +1454,16 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
                active_open_err(cm_node, skb, reset);
                break;
        case NES_CM_STATE_CLOSED:
-               cleanup_retrans_entry(cm_node);
                drop_packet(skb);
                break;
+       case NES_CM_STATE_FIN_WAIT1:
+       case NES_CM_STATE_LAST_ACK:
+               cm_node->cm_id->rem_ref(cm_node->cm_id);
        case NES_CM_STATE_TIME_WAIT:
-               cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_CLOSED;
                rem_ref_cm_node(cm_node->cm_core, cm_node);
                drop_packet(skb);
                break;
-       case NES_CM_STATE_FIN_WAIT1:
-               cleanup_retrans_entry(cm_node);
-               nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__);
        default:
                drop_packet(skb);
                break;
@@ -1451,6 +1509,7 @@ static void handle_rcv_mpa(struct nes_cm_node *cm_node, struct sk_buff *skb)
                                NES_PASSIVE_STATE_INDICATED);
                break;
        case NES_CM_STATE_MPAREQ_SENT:
+               cleanup_retrans_entry(cm_node);
                if (res_type == NES_MPA_REQUEST_REJECT) {
                        type = NES_CM_EVENT_MPA_REJECT;
                        cm_node->state = NES_CM_STATE_MPAREJ_RCVD;
@@ -1514,7 +1573,7 @@ static int check_seq(struct nes_cm_node *cm_node, struct tcphdr *tcph,
        rcv_wnd = cm_node->tcp_cntxt.rcv_wnd;
        if (ack_seq != loc_seq_num)
                err = 1;
-       else if ((seq + rcv_wnd) < rcv_nxt)
+       else if (!between(seq, rcv_nxt, (rcv_nxt+rcv_wnd)))
                err = 1;
        if (err) {
                nes_debug(NES_DBG_CM, "%s[%u] create abort for cm_node=%p "
@@ -1578,6 +1637,7 @@ static void handle_syn_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
                break;
        case NES_CM_STATE_CLOSED:
                cleanup_retrans_entry(cm_node);
+               add_ref_cm_node(cm_node);
                send_reset(cm_node, skb);
                break;
        case NES_CM_STATE_TSA:
@@ -1629,9 +1689,15 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
                passive_open_err(cm_node, skb, 1);
                break;
        case NES_CM_STATE_LISTENING:
+               cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
+               cleanup_retrans_entry(cm_node);
+               cm_node->state = NES_CM_STATE_CLOSED;
+               send_reset(cm_node, skb);
+               break;
        case NES_CM_STATE_CLOSED:
                cm_node->tcp_cntxt.loc_seq_num = ntohl(tcph->ack_seq);
                cleanup_retrans_entry(cm_node);
+               add_ref_cm_node(cm_node);
                send_reset(cm_node, skb);
                break;
        case NES_CM_STATE_ESTABLISHED:
@@ -1648,49 +1714,39 @@ static void handle_synack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
        }
 }
 
-static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
+static int handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
        struct tcphdr *tcph)
 {
        int datasize = 0;
        u32 inc_sequence;
        u32 rem_seq_ack;
        u32 rem_seq;
-       int ret;
+       int ret = 0;
        int optionsize;
        optionsize = (tcph->doff << 2) - sizeof(struct tcphdr);
 
        if (check_seq(cm_node, tcph, skb))
-               return;
+               return -EINVAL;
 
        skb_pull(skb, tcph->doff << 2);
        inc_sequence = ntohl(tcph->seq);
        rem_seq = ntohl(tcph->seq);
        rem_seq_ack =  ntohl(tcph->ack_seq);
        datasize = skb->len;
-       cleanup_retrans_entry(cm_node);
        switch (cm_node->state) {
        case NES_CM_STATE_SYN_RCVD:
                /* Passive OPEN */
+               cleanup_retrans_entry(cm_node);
                ret = handle_tcp_options(cm_node, tcph, skb, optionsize, 1);
                if (ret)
                        break;
                cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
-               if (cm_node->tcp_cntxt.rem_ack_num !=
-                   cm_node->tcp_cntxt.loc_seq_num) {
-                       nes_debug(NES_DBG_CM, "rem_ack_num != loc_seq_num\n");
-                       cleanup_retrans_entry(cm_node);
-                       send_reset(cm_node, skb);
-                       return;
-               }
                cm_node->state = NES_CM_STATE_ESTABLISHED;
-               cleanup_retrans_entry(cm_node);
                if (datasize) {
                        cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
                        handle_rcv_mpa(cm_node, skb);
-               } else { /* rcvd ACK only */
+               } else  /* rcvd ACK only */
                        dev_kfree_skb_any(skb);
-                       cleanup_retrans_entry(cm_node);
-                }
                break;
        case NES_CM_STATE_ESTABLISHED:
                /* Passive OPEN */
@@ -1702,27 +1758,28 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
                        drop_packet(skb);
                break;
        case NES_CM_STATE_MPAREQ_SENT:
-               cleanup_retrans_entry(cm_node);
                cm_node->tcp_cntxt.rem_ack_num = ntohl(tcph->ack_seq);
                if (datasize) {
                        cm_node->tcp_cntxt.rcv_nxt = inc_sequence + datasize;
                        handle_rcv_mpa(cm_node, skb);
-               } else { /* Could be just an ack pkt.. */
-                       cleanup_retrans_entry(cm_node);
+               } else  /* Could be just an ack pkt.. */
                        dev_kfree_skb_any(skb);
-               }
                break;
        case NES_CM_STATE_LISTENING:
+               cleanup_retrans_entry(cm_node);
+               cm_node->state = NES_CM_STATE_CLOSED;
+               send_reset(cm_node, skb);
+               break;
        case NES_CM_STATE_CLOSED:
                cleanup_retrans_entry(cm_node);
+               add_ref_cm_node(cm_node);
                send_reset(cm_node, skb);
                break;
        case NES_CM_STATE_LAST_ACK:
+       case NES_CM_STATE_CLOSING:
                cleanup_retrans_entry(cm_node);
                cm_node->state = NES_CM_STATE_CLOSED;
                cm_node->cm_id->rem_ref(cm_node->cm_id);
-       case NES_CM_STATE_CLOSING:
-               cleanup_retrans_entry(cm_node);
                rem_ref_cm_node(cm_node->cm_core, cm_node);
                drop_packet(skb);
                break;
@@ -1737,9 +1794,11 @@ static void handle_ack_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb,
        case NES_CM_STATE_MPAREQ_RCVD:
        case NES_CM_STATE_UNKNOWN:
        default:
+               cleanup_retrans_entry(cm_node);
                drop_packet(skb);
                break;
        }
+       return ret;
 }
 
 
@@ -1845,6 +1904,7 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
        enum nes_tcpip_pkt_type pkt_type = NES_PKT_TYPE_UNKNOWN;
        struct tcphdr *tcph = tcp_hdr(skb);
        u32     fin_set = 0;
+       int ret = 0;
        skb_pull(skb, ip_hdr(skb)->ihl << 2);
 
        nes_debug(NES_DBG_CM, "process_packet: cm_node=%p state =%d syn=%d "
@@ -1870,17 +1930,17 @@ static void process_packet(struct nes_cm_node *cm_node, struct sk_buff *skb,
                handle_synack_pkt(cm_node, skb, tcph);
                break;
        case NES_PKT_TYPE_ACK:
-               handle_ack_pkt(cm_node, skb, tcph);
-               if (fin_set)
+               ret = handle_ack_pkt(cm_node, skb, tcph);
+               if (fin_set && !ret)
                        handle_fin_pkt(cm_node);
                break;
        case NES_PKT_TYPE_RST:
                handle_rst_pkt(cm_node, skb, tcph);
                break;
        default:
-               drop_packet(skb);
-               if (fin_set)
+               if ((fin_set) && (!check_seq(cm_node, tcph, skb)))
                        handle_fin_pkt(cm_node);
+               drop_packet(skb);
                break;
        }
 }
@@ -1973,7 +2033,7 @@ static struct nes_cm_node *mini_cm_connect(struct nes_cm_core *cm_core,
        if (!cm_node)
                return NULL;
        mpa_frame = &cm_node->mpa_frame;
-       strcpy(mpa_frame->key, IEFT_MPA_KEY_REQ);
+       memcpy(mpa_frame->key, IEFT_MPA_KEY_REQ, IETF_MPA_KEY_SIZE);
        mpa_frame->flags = IETF_MPA_FLAGS_CRC;
        mpa_frame->rev =  IETF_MPA_VERSION;
        mpa_frame->priv_data_len = htons(private_data_len);
@@ -2101,30 +2161,39 @@ static int mini_cm_reject(struct nes_cm_core *cm_core,
                        cm_node->state = NES_CM_STATE_CLOSED;
                        rem_ref_cm_node(cm_core, cm_node);
                } else {
-                       ret = send_mpa_reject(cm_node);
-                       if (ret) {
-                               cm_node->state = NES_CM_STATE_CLOSED;
-                               err = send_reset(cm_node, NULL);
-                               if (err)
-                                       WARN_ON(1);
-                       } else
-                               cm_id->add_ref(cm_id);
+                       if (cm_node->state == NES_CM_STATE_LISTENER_DESTROYED) {
+                               rem_ref_cm_node(cm_core, cm_node);
+                       } else {
+                               ret = send_mpa_reject(cm_node);
+                               if (ret) {
+                                       cm_node->state = NES_CM_STATE_CLOSED;
+                                       err = send_reset(cm_node, NULL);
+                                       if (err)
+                                               WARN_ON(1);
+                               } else
+                                       cm_id->add_ref(cm_id);
+                       }
                }
        } else {
                cm_node->cm_id = NULL;
-               event.cm_node = loopback;
-               event.cm_info.rem_addr = loopback->rem_addr;
-               event.cm_info.loc_addr = loopback->loc_addr;
-               event.cm_info.rem_port = loopback->rem_port;
-               event.cm_info.loc_port = loopback->loc_port;
-               event.cm_info.cm_id = loopback->cm_id;
-               cm_event_mpa_reject(&event);
-               rem_ref_cm_node(cm_core, cm_node);
-               loopback->state = NES_CM_STATE_CLOSING;
+               if (cm_node->state == NES_CM_STATE_LISTENER_DESTROYED) {
+                       rem_ref_cm_node(cm_core, cm_node);
+                       rem_ref_cm_node(cm_core, loopback);
+               } else {
+                       event.cm_node = loopback;
+                       event.cm_info.rem_addr = loopback->rem_addr;
+                       event.cm_info.loc_addr = loopback->loc_addr;
+                       event.cm_info.rem_port = loopback->rem_port;
+                       event.cm_info.loc_port = loopback->loc_port;
+                       event.cm_info.cm_id = loopback->cm_id;
+                       cm_event_mpa_reject(&event);
+                       rem_ref_cm_node(cm_core, cm_node);
+                       loopback->state = NES_CM_STATE_CLOSING;
 
-               cm_id = loopback->cm_id;
-               rem_ref_cm_node(cm_core, loopback);
-               cm_id->rem_ref(cm_id);
+                       cm_id = loopback->cm_id;
+                       rem_ref_cm_node(cm_core, loopback);
+                       cm_id->rem_ref(cm_id);
+               }
        }
 
        return ret;
@@ -2163,11 +2232,15 @@ static int mini_cm_close(struct nes_cm_core *cm_core, struct nes_cm_node *cm_nod
        case NES_CM_STATE_CLOSING:
                ret = -1;
                break;
-       case NES_CM_STATE_MPAREJ_RCVD:
        case NES_CM_STATE_LISTENING:
+               cleanup_retrans_entry(cm_node);
+               send_reset(cm_node, NULL);
+               break;
+       case NES_CM_STATE_MPAREJ_RCVD:
        case NES_CM_STATE_UNKNOWN:
        case NES_CM_STATE_INITED:
        case NES_CM_STATE_CLOSED:
+       case NES_CM_STATE_LISTENER_DESTROYED:
                ret = rem_ref_cm_node(cm_core, cm_node);
                break;
        case NES_CM_STATE_TSA:
@@ -2449,19 +2522,16 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
  */
 int nes_cm_disconn(struct nes_qp *nesqp)
 {
-       unsigned long flags;
+       struct disconn_work *work;
 
-       spin_lock_irqsave(&nesqp->lock, flags);
-       if (nesqp->disconn_pending == 0) {
-               nesqp->disconn_pending++;
-               spin_unlock_irqrestore(&nesqp->lock, flags);
-               /* init our disconnect work element, to */
-               INIT_WORK(&nesqp->disconn_work, nes_disconnect_worker);
-
-               queue_work(g_cm_core->disconn_wq, &nesqp->disconn_work);
-       } else
-               spin_unlock_irqrestore(&nesqp->lock, flags);
+       work = kzalloc(sizeof *work, GFP_ATOMIC);
+       if (!work)
+               return -ENOMEM; /* Timer will clean up */
 
+       nes_add_ref(&nesqp->ibqp);
+       work->nesqp = nesqp;
+       INIT_WORK(&work->work, nes_disconnect_worker);
+       queue_work(g_cm_core->disconn_wq, &work->work);
        return 0;
 }
 
@@ -2471,11 +2541,14 @@ int nes_cm_disconn(struct nes_qp *nesqp)
  */
 static void nes_disconnect_worker(struct work_struct *work)
 {
-       struct nes_qp *nesqp = container_of(work, struct nes_qp, disconn_work);
+       struct disconn_work *dwork = container_of(work, struct disconn_work, work);
+       struct nes_qp *nesqp = dwork->nesqp;
 
+       kfree(dwork);
        nes_debug(NES_DBG_CM, "processing AEQE id 0x%04X for QP%u.\n",
                        nesqp->last_aeq, nesqp->hwqp.qp_id);
        nes_cm_disconn_true(nesqp);
+       nes_rem_ref(&nesqp->ibqp);
 }
 
 
@@ -2492,7 +2565,12 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
        u16 last_ae;
        u8 original_hw_tcp_state;
        u8 original_ibqp_state;
-       u8 issued_disconnect_reset = 0;
+       enum iw_cm_event_type disconn_status = IW_CM_EVENT_STATUS_OK;
+       int issue_disconn = 0;
+       int issue_close = 0;
+       int issue_flush = 0;
+       u32 flush_q = NES_CQP_FLUSH_RQ;
+       struct ib_event ibevent;
 
        if (!nesqp) {
                nes_debug(NES_DBG_CM, "disconnect_worker nesqp is NULL\n");
@@ -2516,24 +2594,55 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
        original_ibqp_state   = nesqp->ibqp_state;
        last_ae = nesqp->last_aeq;
 
+       if (nesqp->term_flags) {
+               issue_disconn = 1;
+               issue_close = 1;
+               nesqp->cm_id = NULL;
+               if (nesqp->flush_issued == 0) {
+                       nesqp->flush_issued = 1;
+                       issue_flush = 1;
+               }
+       } else if ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
+                       ((original_ibqp_state == IB_QPS_RTS) &&
+                       (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+               issue_disconn = 1;
+               if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET)
+                       disconn_status = IW_CM_EVENT_STATUS_RESET;
+       }
+
+       if (((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
+                (original_hw_tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT) ||
+                (last_ae == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) ||
+                (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+               issue_close = 1;
+               nesqp->cm_id = NULL;
+               if (nesqp->flush_issued == 0) {
+                       nesqp->flush_issued = 1;
+                       issue_flush = 1;
+               }
+       }
+
+       spin_unlock_irqrestore(&nesqp->lock, flags);
 
-       nes_debug(NES_DBG_CM, "set ibqp_state=%u\n", nesqp->ibqp_state);
+       if ((issue_flush) && (nesqp->destroyed == 0)) {
+               /* Flush the queue(s) */
+               if (nesqp->hw_iwarp_state >= NES_AEQE_IWARP_STATE_TERMINATE)
+                       flush_q |= NES_CQP_FLUSH_SQ;
+               flush_wqes(nesvnic->nesdev, nesqp, flush_q, 1);
 
-       if ((nesqp->cm_id) && (cm_id->event_handler)) {
-               if ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
-                               ((original_ibqp_state == IB_QPS_RTS) &&
-                               (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+               if (nesqp->term_flags) {
+                       ibevent.device = nesqp->ibqp.device;
+                       ibevent.event = nesqp->terminate_eventtype;
+                       ibevent.element.qp = &nesqp->ibqp;
+                       nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+               }
+       }
+
+       if ((cm_id) && (cm_id->event_handler)) {
+               if (issue_disconn) {
                        atomic_inc(&cm_disconnects);
                        cm_event.event = IW_CM_EVENT_DISCONNECT;
-                       if (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET) {
-                               cm_event.status = IW_CM_EVENT_STATUS_RESET;
-                               nes_debug(NES_DBG_CM, "Generating a CM "
-                                       "Disconnect Event (status reset) for "
-                                       "QP%u, cm_id = %p. \n",
-                                       nesqp->hwqp.qp_id, cm_id);
-                       } else
-                               cm_event.status = IW_CM_EVENT_STATUS_OK;
-
+                       cm_event.status = disconn_status;
                        cm_event.local_addr = cm_id->local_addr;
                        cm_event.remote_addr = cm_id->remote_addr;
                        cm_event.private_data = NULL;
@@ -2546,29 +2655,14 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
                                nesqp->hwqp.sq_tail, cm_id,
                                atomic_read(&nesqp->refcount));
 
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
                        ret = cm_id->event_handler(cm_id, &cm_event);
                        if (ret)
                                nes_debug(NES_DBG_CM, "OFA CM event_handler "
                                        "returned, ret=%d\n", ret);
-                       spin_lock_irqsave(&nesqp->lock, flags);
                }
 
-               nesqp->disconn_pending = 0;
-               /* There might have been another AE while the lock was released */
-               original_hw_tcp_state = nesqp->hw_tcp_state;
-               original_ibqp_state   = nesqp->ibqp_state;
-               last_ae = nesqp->last_aeq;
-
-               if ((issued_disconnect_reset == 0) && (nesqp->cm_id) &&
-                               ((original_hw_tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
-                                (original_hw_tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT) ||
-                                (last_ae == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) ||
-                                (last_ae == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+               if (issue_close) {
                        atomic_inc(&cm_closes);
-                       nesqp->cm_id = NULL;
-                       nesqp->in_disconnect = 0;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
                        nes_disconnect(nesqp, 1);
 
                        cm_id->provider_data = nesqp;
@@ -2587,28 +2681,7 @@ static int nes_cm_disconn_true(struct nes_qp *nesqp)
                        }
 
                        cm_id->rem_ref(cm_id);
-
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       if (nesqp->flush_issued == 0) {
-                               nesqp->flush_issued = 1;
-                               spin_unlock_irqrestore(&nesqp->lock, flags);
-                               flush_wqes(nesvnic->nesdev, nesqp,
-                                       NES_CQP_FLUSH_RQ, 1);
-                       } else
-                               spin_unlock_irqrestore(&nesqp->lock, flags);
-               } else {
-                       cm_id = nesqp->cm_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       /* check to see if the inbound reset beat the outbound reset */
-                       if ((!cm_id) && (last_ae==NES_AEQE_AEID_RESET_SENT)) {
-                               nes_debug(NES_DBG_CM, "QP%u: Decing refcount "
-                                       "due to inbound reset beating the "
-                                       "outbound reset.\n", nesqp->hwqp.qp_id);
-                       }
                }
-       } else {
-               nesqp->disconn_pending = 0;
-               spin_unlock_irqrestore(&nesqp->lock, flags);
        }
 
        return 0;
@@ -2686,8 +2759,6 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        struct nes_pd *nespd;
        u64 tagged_offset;
 
-
-
        ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
        if (!ibqp)
                return -EINVAL;
@@ -2703,10 +2774,16 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                "%s\n", cm_node, nesvnic, nesvnic->netdev,
                nesvnic->netdev->name);
 
+       if (NES_CM_STATE_LISTENER_DESTROYED == cm_node->state) {
+               if (cm_node->loopbackpartner)
+                       rem_ref_cm_node(cm_node->cm_core, cm_node->loopbackpartner);
+               rem_ref_cm_node(cm_node->cm_core, cm_node);
+               return -EINVAL;
+       }
+
        /* associate the node with the QP */
        nesqp->cm_node = (void *)cm_node;
        cm_node->nesqp = nesqp;
-       nes_add_ref(&nesqp->ibqp);
 
        nes_debug(NES_DBG_CM, "QP%u, cm_node=%p, jiffies = %lu listener = %p\n",
                nesqp->hwqp.qp_id, cm_node, jiffies, cm_node->listener);
@@ -2759,6 +2836,9 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                        nes_debug(NES_DBG_CM, "Unable to register memory region"
                                        "for lSMM for cm_node = %p \n",
                                        cm_node);
+                       pci_free_consistent(nesdev->pcidev,
+                               nesqp->private_data_len+sizeof(struct ietf_mpa_frame),
+                               nesqp->ietf_frame, nesqp->ietf_frame_pbase);
                        return -ENOMEM;
                }
 
@@ -2783,6 +2863,10 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                        cpu_to_le32(conn_param->private_data_len +
                        sizeof(struct ietf_mpa_frame));
                wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = ibmr->lkey;
+               if (nesqp->sq_kmapped) {
+                       nesqp->sq_kmapped = 0;
+                       kunmap(nesqp->page);
+               }
 
                nesqp->nesqp_context->ird_ord_sizes |=
                        cpu_to_le32(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
@@ -2875,6 +2959,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
 
        /* notify OF layer that accept event was successful */
        cm_id->add_ref(cm_id);
+       nes_add_ref(&nesqp->ibqp);
 
        cm_event.event = IW_CM_EVENT_ESTABLISHED;
        cm_event.status = IW_CM_EVENT_STATUS_ACCEPTED;
@@ -2925,7 +3010,7 @@ int nes_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
        if (cm_node->mpa_frame_size > MAX_CM_BUFFER)
                return -EINVAL;
 
-       strcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP);
+       memcpy(&cm_node->mpa_frame.key[0], IEFT_MPA_KEY_REP, IETF_MPA_KEY_SIZE);
        if (loopback) {
                memcpy(&loopback->mpa_frame.priv_data, pdata, pdata_len);
                loopback->mpa_frame.priv_data_len = pdata_len;
@@ -2955,6 +3040,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        struct nes_device *nesdev;
        struct nes_cm_node *cm_node;
        struct nes_cm_info cm_info;
+       int apbvt_set = 0;
 
        ibqp = nes_get_qp(cm_id->device, conn_param->qpn);
        if (!ibqp)
@@ -2969,6 +3055,9 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
        if (!nesdev)
                return -EINVAL;
 
+       if (!(cm_id->local_addr.sin_port) || !(cm_id->remote_addr.sin_port))
+               return -EINVAL;
+
        nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = "
                "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id,
                ntohl(nesvnic->local_ipaddr),
@@ -2992,9 +3081,11 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                conn_param->private_data_len);
 
        if (cm_id->local_addr.sin_addr.s_addr !=
-               cm_id->remote_addr.sin_addr.s_addr)
+               cm_id->remote_addr.sin_addr.s_addr) {
                nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
                        PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD);
+               apbvt_set = 1;
+       }
 
        /* set up the connection params for the node */
        cm_info.loc_addr = htonl(cm_id->local_addr.sin_addr.s_addr);
@@ -3011,8 +3102,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                conn_param->private_data_len, (void *)conn_param->private_data,
                &cm_info);
        if (!cm_node) {
-               if (cm_id->local_addr.sin_addr.s_addr !=
-                               cm_id->remote_addr.sin_addr.s_addr)
+               if (apbvt_set)
                        nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port),
                                PCI_FUNC(nesdev->pcidev->devfn),
                                NES_MANAGE_APBVT_DEL);
@@ -3021,7 +3111,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                return -ENOMEM;
        }
 
-       cm_node->apbvt_set = 1;
+       cm_node->apbvt_set = apbvt_set;
        nesqp->cm_node = cm_node;
        cm_node->nesqp = nesqp;
        nes_add_ref(&nesqp->ibqp);
@@ -3084,7 +3174,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
                        g_cm_core->api->stop_listener(g_cm_core, (void *)cm_node);
                        return err;
                }
-               cm_listens_created++;
+               atomic_inc(&cm_listens_created);
        }
 
        cm_id->add_ref(cm_id);
@@ -3245,6 +3335,11 @@ static void cm_event_connected(struct nes_cm_event *event)
                wqe->wqe_words[NES_IWARP_SQ_WQE_LENGTH0_IDX] = 0;
                wqe->wqe_words[NES_IWARP_SQ_WQE_STAG0_IDX] = 0;
 
+               if (nesqp->sq_kmapped) {
+                       nesqp->sq_kmapped = 0;
+                       kunmap(nesqp->page);
+               }
+
                /* use the reserved spot on the WQ for the extra first WQE */
                nesqp->nesqp_context->ird_ord_sizes &=
                        cpu_to_le32(~(NES_QPCONTEXT_ORDIRD_LSMM_PRESENT |
@@ -3340,7 +3435,7 @@ static void cm_event_connect_error(struct nes_cm_event *event)
        nesqp->cm_id = NULL;
        cm_id->provider_data = NULL;
        cm_event.event = IW_CM_EVENT_CONNECT_REPLY;
-       cm_event.status = IW_CM_EVENT_STATUS_REJECTED;
+       cm_event.status = -ECONNRESET;
        cm_event.provider_data = cm_id->provider_data;
        cm_event.local_addr = cm_id->local_addr;
        cm_event.remote_addr = cm_id->remote_addr;
@@ -3384,6 +3479,8 @@ static void cm_event_reset(struct nes_cm_event *event)
 
        nes_debug(NES_DBG_CM, "%p - cm_id = %p\n", event->cm_node, cm_id);
        nesqp = cm_id->provider_data;
+       if (!nesqp)
+               return;
 
        nesqp->cm_id = NULL;
        /* cm_id->provider_data = NULL; */
@@ -3395,8 +3492,8 @@ static void cm_event_reset(struct nes_cm_event *event)
        cm_event.private_data = NULL;
        cm_event.private_data_len = 0;
 
-       ret = cm_id->event_handler(cm_id, &cm_event);
        cm_id->add_ref(cm_id);
+       ret = cm_id->event_handler(cm_id, &cm_event);
        atomic_inc(&cm_closes);
        cm_event.event = IW_CM_EVENT_CLOSE;
        cm_event.status = IW_CM_EVENT_STATUS_OK;