RDMA/nes: Implement Terminate Packet
authorDon Wood <donald.e.wood@intel.com>
Sun, 6 Sep 2009 03:36:38 +0000 (20:36 -0700)
committerRoland Dreier <rolandd@cisco.com>
Sun, 6 Sep 2009 03:36:38 +0000 (20:36 -0700)
Implement the sending and receiving of Terminate packets.

Signed-off-by: Don Wood <donald.e.wood@intel.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/hw/nes/nes.h
drivers/infiniband/hw/nes/nes_cm.h
drivers/infiniband/hw/nes/nes_hw.c
drivers/infiniband/hw/nes/nes_hw.h
drivers/infiniband/hw/nes/nes_utils.c
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/hw/nes/nes_verbs.h

index bf1720f..bcc6abc 100644 (file)
@@ -523,7 +523,7 @@ int nes_cm_disconn(struct nes_qp *);
 void nes_cm_disconn_worker(void *);
 
 /* nes_verbs.c */
-int nes_hw_modify_qp(struct nes_device *, struct nes_qp *, u32, u32);
+int nes_hw_modify_qp(struct nes_device *, struct nes_qp *, u32, u32, u32);
 int nes_modify_qp(struct ib_qp *, struct ib_qp_attr *, int, struct ib_udata *);
 struct nes_ib_device *nes_init_ofa_device(struct net_device *);
 void nes_destroy_ofa_device(struct nes_ib_device *);
index 8b7e7c0..90e8e4d 100644 (file)
@@ -410,8 +410,6 @@ struct nes_cm_ops {
 int schedule_nes_timer(struct nes_cm_node *, struct sk_buff *,
                enum nes_timer_type, int, int);
 
-int nes_cm_disconn(struct nes_qp *);
-
 int nes_accept(struct iw_cm_id *, struct iw_cm_conn_param *);
 int nes_reject(struct iw_cm_id *, const void *, u8);
 int nes_connect(struct iw_cm_id *, struct iw_cm_conn_param *);
index 2a0c5a1..297026f 100644 (file)
@@ -74,6 +74,8 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
 static void process_critical_error(struct nes_device *nesdev);
 static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number);
 static unsigned int nes_reset_adapter_ne020(struct nes_device *nesdev, u8 *OneG_Mode);
+static void nes_terminate_timeout(unsigned long context);
+static void nes_terminate_start_timer(struct nes_qp *nesqp);
 
 #ifdef CONFIG_INFINIBAND_NES_DEBUG
 static unsigned char *nes_iwarp_state_str[] = {
@@ -2903,6 +2905,383 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)
 }
 
 
+static u8 *locate_mpa(u8 *pkt, u32 aeq_info)
+{
+       u16 pkt_len;
+
+       if (aeq_info & NES_AEQE_Q2_DATA_ETHERNET) {
+               /* skip over ethernet header */
+               pkt_len = be16_to_cpu(*(u16 *)(pkt + ETH_HLEN - 2));
+               pkt += ETH_HLEN;
+
+               /* Skip over IP and TCP headers */
+               pkt += 4 * (pkt[0] & 0x0f);
+               pkt += 4 * ((pkt[12] >> 4) & 0x0f);
+       }
+       return pkt;
+}
+
+/* Determine if incoming error pkt is rdma layer */
+static u32 iwarp_opcode(struct nes_qp *nesqp, u32 aeq_info)
+{
+       u8 *pkt;
+       u16 *mpa;
+       u32 opcode = 0xffffffff;
+
+       if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) {
+               pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET;
+               mpa = (u16 *)locate_mpa(pkt, aeq_info);
+               opcode = be16_to_cpu(mpa[1]) & 0xf;
+       }
+
+       return opcode;
+}
+
+/* Build iWARP terminate header */
+static int nes_bld_terminate_hdr(struct nes_qp *nesqp, u16 async_event_id, u32 aeq_info)
+{
+       u8 *pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET;
+       u16 ddp_seg_len;
+       int copy_len = 0;
+       u8 is_tagged = 0;
+       struct nes_terminate_hdr *termhdr;
+
+       termhdr = (struct nes_terminate_hdr *)nesqp->hwqp.q2_vbase;
+       memset(termhdr, 0, 64);
+
+       if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) {
+
+               /* Use data from offending packet to fill in ddp & rdma hdrs */
+               pkt = locate_mpa(pkt, aeq_info);
+               ddp_seg_len = be16_to_cpu(*(u16 *)pkt);
+               if (ddp_seg_len) {
+                       copy_len = 2;
+                       termhdr->hdrct = DDP_LEN_FLAG;
+                       if (pkt[2] & 0x80) {
+                               is_tagged = 1;
+                               if (ddp_seg_len >= TERM_DDP_LEN_TAGGED) {
+                                       copy_len += TERM_DDP_LEN_TAGGED;
+                                       termhdr->hdrct |= DDP_HDR_FLAG;
+                               }
+                       } else {
+                               if (ddp_seg_len >= TERM_DDP_LEN_UNTAGGED) {
+                                       copy_len += TERM_DDP_LEN_UNTAGGED;
+                                       termhdr->hdrct |= DDP_HDR_FLAG;
+                               }
+
+                               if (ddp_seg_len >= (TERM_DDP_LEN_UNTAGGED + TERM_RDMA_LEN)) {
+                                       if ((pkt[3] & RDMA_OPCODE_MASK) == RDMA_READ_REQ_OPCODE) {
+                                               copy_len += TERM_RDMA_LEN;
+                                               termhdr->hdrct |= RDMA_HDR_FLAG;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       switch (async_event_id) {
+       case NES_AEQE_AEID_AMP_UNALLOCATED_STAG:
+               switch (iwarp_opcode(nesqp, aeq_info)) {
+               case IWARP_OPCODE_WRITE:
+                       termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER;
+                       termhdr->error_code = DDP_TAGGED_INV_STAG;
+                       break;
+               default:
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+                       termhdr->error_code = RDMAP_INV_STAG;
+               }
+               break;
+       case NES_AEQE_AEID_AMP_INVALID_STAG:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+               termhdr->error_code = RDMAP_INV_STAG;
+               break;
+       case NES_AEQE_AEID_AMP_BAD_QP:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_QN;
+               break;
+       case NES_AEQE_AEID_AMP_BAD_STAG_KEY:
+       case NES_AEQE_AEID_AMP_BAD_STAG_INDEX:
+               switch (iwarp_opcode(nesqp, aeq_info)) {
+               case IWARP_OPCODE_SEND_INV:
+               case IWARP_OPCODE_SEND_SE_INV:
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP;
+                       termhdr->error_code = RDMAP_CANT_INV_STAG;
+                       break;
+               default:
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+                       termhdr->error_code = RDMAP_INV_STAG;
+               }
+               break;
+       case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION:
+               if (aeq_info & (NES_AEQE_Q2_DATA_ETHERNET | NES_AEQE_Q2_DATA_MPA)) {
+                       termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER;
+                       termhdr->error_code = DDP_TAGGED_BOUNDS;
+               } else {
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+                       termhdr->error_code = RDMAP_INV_BOUNDS;
+               }
+               break;
+       case NES_AEQE_AEID_AMP_RIGHTS_VIOLATION:
+       case NES_AEQE_AEID_AMP_INVALIDATE_NO_REMOTE_ACCESS_RIGHTS:
+       case NES_AEQE_AEID_PRIV_OPERATION_DENIED:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+               termhdr->error_code = RDMAP_ACCESS;
+               break;
+       case NES_AEQE_AEID_AMP_TO_WRAP:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+               termhdr->error_code = RDMAP_TO_WRAP;
+               break;
+       case NES_AEQE_AEID_AMP_BAD_PD:
+               switch (iwarp_opcode(nesqp, aeq_info)) {
+               case IWARP_OPCODE_WRITE:
+                       termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER;
+                       termhdr->error_code = DDP_TAGGED_UNASSOC_STAG;
+                       break;
+               case IWARP_OPCODE_SEND_INV:
+               case IWARP_OPCODE_SEND_SE_INV:
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+                       termhdr->error_code = RDMAP_CANT_INV_STAG;
+                       break;
+               default:
+                       termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_PROT;
+                       termhdr->error_code = RDMAP_UNASSOC_STAG;
+               }
+               break;
+       case NES_AEQE_AEID_LLP_RECEIVED_MARKER_AND_LENGTH_FIELDS_DONT_MATCH:
+               termhdr->layer_etype = (LAYER_MPA << 4) | DDP_LLP;
+               termhdr->error_code = MPA_MARKER;
+               break;
+       case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR:
+               termhdr->layer_etype = (LAYER_MPA << 4) | DDP_LLP;
+               termhdr->error_code = MPA_CRC;
+               break;
+       case NES_AEQE_AEID_LLP_SEGMENT_TOO_LARGE:
+       case NES_AEQE_AEID_LLP_SEGMENT_TOO_SMALL:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_CATASTROPHIC;
+               termhdr->error_code = DDP_CATASTROPHIC_LOCAL;
+               break;
+       case NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC:
+       case NES_AEQE_AEID_DDP_NO_L_BIT:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_CATASTROPHIC;
+               termhdr->error_code = DDP_CATASTROPHIC_LOCAL;
+               break;
+       case NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN:
+       case NES_AEQE_AEID_DDP_INVALID_MSN_RANGE_IS_NOT_VALID:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_MSN_RANGE;
+               break;
+       case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_TOO_LONG;
+               break;
+       case NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION:
+               if (is_tagged) {
+                       termhdr->layer_etype = (LAYER_DDP << 4) | DDP_TAGGED_BUFFER;
+                       termhdr->error_code = DDP_TAGGED_INV_DDP_VER;
+               } else {
+                       termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+                       termhdr->error_code = DDP_UNTAGGED_INV_DDP_VER;
+               }
+               break;
+       case NES_AEQE_AEID_DDP_UBE_INVALID_MO:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_MO;
+               break;
+       case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_MSN_NO_BUF;
+               break;
+       case NES_AEQE_AEID_DDP_UBE_INVALID_QN:
+               termhdr->layer_etype = (LAYER_DDP << 4) | DDP_UNTAGGED_BUFFER;
+               termhdr->error_code = DDP_UNTAGGED_INV_QN;
+               break;
+       case NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP;
+               termhdr->error_code = RDMAP_INV_RDMAP_VER;
+               break;
+       case NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP;
+               termhdr->error_code = RDMAP_UNEXPECTED_OP;
+               break;
+       default:
+               termhdr->layer_etype = (LAYER_RDMA << 4) | RDMAP_REMOTE_OP;
+               termhdr->error_code = RDMAP_UNSPECIFIED;
+               break;
+       }
+
+       if (copy_len)
+               memcpy(termhdr + 1, pkt, copy_len);
+
+       return sizeof(struct nes_terminate_hdr) + copy_len;
+}
+
+static void nes_terminate_connection(struct nes_device *nesdev, struct nes_qp *nesqp,
+                struct nes_hw_aeqe *aeqe, enum ib_event_type eventtype)
+{
+       u64 context;
+       unsigned long flags;
+       u32 aeq_info;
+       u16 async_event_id;
+       u8 tcp_state;
+       u8 iwarp_state;
+       u32 termlen = 0;
+       u32 mod_qp_flags = NES_CQP_QP_IWARP_STATE_TERMINATE |
+                          NES_CQP_QP_TERM_DONT_SEND_FIN;
+       struct nes_adapter *nesadapter = nesdev->nesadapter;
+
+       if (nesqp->term_flags & NES_TERM_SENT)
+               return; /* Sanity check */
+
+       aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
+       tcp_state = (aeq_info & NES_AEQE_TCP_STATE_MASK) >> NES_AEQE_TCP_STATE_SHIFT;
+       iwarp_state = (aeq_info & NES_AEQE_IWARP_STATE_MASK) >> NES_AEQE_IWARP_STATE_SHIFT;
+       async_event_id = (u16)aeq_info;
+
+       context = (unsigned long)nesadapter->qp_table[le32_to_cpu(
+               aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]) - NES_FIRST_QPN];
+       if (!context) {
+               WARN_ON(!context);
+               return;
+       }
+
+       nesqp = (struct nes_qp *)(unsigned long)context;
+       spin_lock_irqsave(&nesqp->lock, flags);
+       nesqp->hw_iwarp_state = iwarp_state;
+       nesqp->hw_tcp_state = tcp_state;
+       nesqp->last_aeq = async_event_id;
+       nesqp->terminate_eventtype = eventtype;
+       spin_unlock_irqrestore(&nesqp->lock, flags);
+
+       if (nesadapter->send_term_ok)
+               termlen = nes_bld_terminate_hdr(nesqp, async_event_id, aeq_info);
+       else
+               mod_qp_flags |= NES_CQP_QP_TERM_DONT_SEND_TERM_MSG;
+
+       nes_terminate_start_timer(nesqp);
+       nesqp->term_flags |= NES_TERM_SENT;
+       nes_hw_modify_qp(nesdev, nesqp, mod_qp_flags, termlen, 0);
+}
+
+static void nes_terminate_send_fin(struct nes_device *nesdev,
+                         struct nes_qp *nesqp, struct nes_hw_aeqe *aeqe)
+{
+       u32 aeq_info;
+       u16 async_event_id;
+       u8 tcp_state;
+       u8 iwarp_state;
+       unsigned long flags;
+
+       aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
+       tcp_state = (aeq_info & NES_AEQE_TCP_STATE_MASK) >> NES_AEQE_TCP_STATE_SHIFT;
+       iwarp_state = (aeq_info & NES_AEQE_IWARP_STATE_MASK) >> NES_AEQE_IWARP_STATE_SHIFT;
+       async_event_id = (u16)aeq_info;
+
+       spin_lock_irqsave(&nesqp->lock, flags);
+       nesqp->hw_iwarp_state = iwarp_state;
+       nesqp->hw_tcp_state = tcp_state;
+       nesqp->last_aeq = async_event_id;
+       spin_unlock_irqrestore(&nesqp->lock, flags);
+
+       /* Send the fin only */
+       nes_hw_modify_qp(nesdev, nesqp, NES_CQP_QP_IWARP_STATE_TERMINATE |
+               NES_CQP_QP_TERM_DONT_SEND_TERM_MSG, 0, 0);
+}
+
+/* Cleanup after a terminate sent or received */
+static void nes_terminate_done(struct nes_qp *nesqp, int timeout_occurred)
+{
+       u32 next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR;
+       unsigned long flags;
+       struct nes_vnic *nesvnic = to_nesvnic(nesqp->ibqp.device);
+       struct nes_device *nesdev = nesvnic->nesdev;
+       u8 first_time = 0;
+
+       spin_lock_irqsave(&nesqp->lock, flags);
+       if (nesqp->hte_added) {
+               nesqp->hte_added = 0;
+               next_iwarp_state |= NES_CQP_QP_DEL_HTE;
+       }
+
+       first_time = (nesqp->term_flags & NES_TERM_DONE) == 0;
+       nesqp->term_flags |= NES_TERM_DONE;
+       spin_unlock_irqrestore(&nesqp->lock, flags);
+
+       /* Make sure we go through this only once */
+       if (first_time) {
+               if (timeout_occurred == 0)
+                       del_timer(&nesqp->terminate_timer);
+               else
+                       next_iwarp_state |= NES_CQP_QP_RESET;
+
+               nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 0);
+               nes_cm_disconn(nesqp);
+       }
+}
+
+static void nes_terminate_received(struct nes_device *nesdev,
+                               struct nes_qp *nesqp, struct nes_hw_aeqe *aeqe)
+{
+       u32 aeq_info;
+       u8 *pkt;
+       u32 *mpa;
+       u8 ddp_ctl;
+       u8 rdma_ctl;
+       u16 aeq_id = 0;
+
+       aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
+       if (aeq_info & NES_AEQE_Q2_DATA_WRITTEN) {
+               /* Terminate is not a performance path so the silicon */
+               /* did not validate the frame - do it now */
+               pkt = nesqp->hwqp.q2_vbase + BAD_FRAME_OFFSET;
+               mpa = (u32 *)locate_mpa(pkt, aeq_info);
+               ddp_ctl = (be32_to_cpu(mpa[0]) >> 8) & 0xff;
+               rdma_ctl = be32_to_cpu(mpa[0]) & 0xff;
+               if ((ddp_ctl & 0xc0) != 0x40)
+                       aeq_id = NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC;
+               else if ((ddp_ctl & 0x03) != 1)
+                       aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION;
+               else if (be32_to_cpu(mpa[2]) != 2)
+                       aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_QN;
+               else if (be32_to_cpu(mpa[3]) != 1)
+                       aeq_id = NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN;
+               else if (be32_to_cpu(mpa[4]) != 0)
+                       aeq_id = NES_AEQE_AEID_DDP_UBE_INVALID_MO;
+               else if ((rdma_ctl & 0xc0) != 0x40)
+                       aeq_id = NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION;
+
+               if (aeq_id) {
+                       /* Bad terminate recvd - send back a terminate */
+                       aeq_info = (aeq_info & 0xffff0000) | aeq_id;
+                       aeqe->aeqe_words[NES_AEQE_MISC_IDX] = cpu_to_le32(aeq_info);
+                       nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
+                       return;
+               }
+       }
+
+       nesqp->term_flags |= NES_TERM_RCVD;
+       nesqp->terminate_eventtype = IB_EVENT_QP_FATAL;
+       nes_terminate_start_timer(nesqp);
+       nes_terminate_send_fin(nesdev, nesqp, aeqe);
+}
+
+/* Timeout routine in case terminate fails to complete */
+static void nes_terminate_timeout(unsigned long context)
+{
+       struct nes_qp *nesqp = (struct nes_qp *)(unsigned long)context;
+
+       nes_terminate_done(nesqp, 1);
+}
+
+/* Set a timer in case hw cannot complete the terminate sequence */
+static void nes_terminate_start_timer(struct nes_qp *nesqp)
+{
+       init_timer(&nesqp->terminate_timer);
+       nesqp->terminate_timer.function = nes_terminate_timeout;
+       nesqp->terminate_timer.expires = jiffies + HZ;
+       nesqp->terminate_timer.data = (unsigned long)nesqp;
+       add_timer(&nesqp->terminate_timer);
+}
+
 /**
  * nes_process_iwarp_aeqe
  */
@@ -2910,30 +3289,27 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                   struct nes_hw_aeqe *aeqe)
 {
        u64 context;
-       u64 aeqe_context = 0;
        unsigned long flags;
        struct nes_qp *nesqp;
        struct nes_hw_cq *hw_cq;
        struct nes_cq *nescq;
        int resource_allocated;
-       /* struct iw_cm_id *cm_id; */
        struct nes_adapter *nesadapter = nesdev->nesadapter;
-       struct ib_event ibevent;
-       /* struct iw_cm_event cm_event; */
        u32 aeq_info;
        u32 next_iwarp_state = 0;
        u16 async_event_id;
        u8 tcp_state;
        u8 iwarp_state;
+       int must_disconn = 1;
+       int must_terminate = 0;
+       struct ib_event ibevent;
 
        nes_debug(NES_DBG_AEQ, "\n");
        aeq_info = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_MISC_IDX]);
-       if ((NES_AEQE_INBOUND_RDMA&aeq_info) || (!(NES_AEQE_QP&aeq_info))) {
+       if ((NES_AEQE_INBOUND_RDMA & aeq_info) || (!(NES_AEQE_QP & aeq_info))) {
                context  = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]);
                context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32;
        } else {
-               aeqe_context = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_LOW_IDX]);
-               aeqe_context += ((u64)le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_CTXT_HIGH_IDX])) << 32;
                context = (unsigned long)nesadapter->qp_table[le32_to_cpu(
                                                aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]) - NES_FIRST_QPN];
                BUG_ON(!context);
@@ -2950,7 +3326,11 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
 
        switch (async_event_id) {
                case NES_AEQE_AEID_LLP_FIN_RECEIVED:
-                       nesqp = *((struct nes_qp **)&context);
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+
+                       if (nesqp->term_flags)
+                               return; /* Ignore it, wait for close complete */
+
                        if (atomic_inc_return(&nesqp->close_timer_started) == 1) {
                                nesqp->cm_id->add_ref(nesqp->cm_id);
                                schedule_nes_timer(nesqp->cm_node, (struct sk_buff *)nesqp,
@@ -2961,18 +3341,24 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                                nesqp->hwqp.qp_id, atomic_read(&nesqp->refcount),
                                                async_event_id, nesqp->last_aeq, tcp_state);
                        }
+
                        if ((tcp_state != NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
                                        (nesqp->ibqp_state != IB_QPS_RTS)) {
                                /* FIN Received but tcp state or IB state moved on,
                                                should expect a close complete */
                                return;
                        }
+
                case NES_AEQE_AEID_LLP_CLOSE_COMPLETE:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       if (nesqp->term_flags) {
+                               nes_terminate_done(nesqp, 0);
+                               return;
+                       }
+
                case NES_AEQE_AEID_LLP_CONNECTION_RESET:
-               case NES_AEQE_AEID_TERMINATE_SENT:
-               case NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE:
                case NES_AEQE_AEID_RESET_SENT:
-                       nesqp = *((struct nes_qp **)&context);
+                       nesqp = (struct nes_qp *)(unsigned long)context;
                        if (async_event_id == NES_AEQE_AEID_RESET_SENT) {
                                tcp_state = NES_AEQE_TCP_STATE_CLOSED;
                        }
@@ -2984,12 +3370,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                        if ((tcp_state == NES_AEQE_TCP_STATE_CLOSED) ||
                                        (tcp_state == NES_AEQE_TCP_STATE_TIME_WAIT)) {
                                nesqp->hte_added = 0;
-                               spin_unlock_irqrestore(&nesqp->lock, flags);
-                               nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u to remove hte\n",
-                                               nesqp->hwqp.qp_id);
-                               nes_hw_modify_qp(nesdev, nesqp,
-                                               NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_DEL_HTE, 0);
-                               spin_lock_irqsave(&nesqp->lock, flags);
+                               next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_DEL_HTE;
                        }
 
                        if ((nesqp->ibqp_state == IB_QPS_RTS) &&
@@ -3001,151 +3382,106 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                                nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING;
                                                break;
                                        case NES_AEQE_IWARP_STATE_TERMINATE:
-                                               next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE;
-                                               nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_TERMINATE;
-                                               if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) {
-                                                       next_iwarp_state |= 0x02000000;
-                                                       nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
-                                               }
+                                               must_disconn = 0; /* terminate path takes care of disconn */
+                                               if (nesqp->term_flags == 0)
+                                                       must_terminate = 1;
                                                break;
-                                       default:
-                                               next_iwarp_state = 0;
-                               }
-                               spin_unlock_irqrestore(&nesqp->lock, flags);
-                               if (next_iwarp_state) {
-                                       nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X,"
-                                                       " also added another reference\n",
-                                                       nesqp->hwqp.qp_id, next_iwarp_state);
-                                       nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
                                }
-                               nes_cm_disconn(nesqp);
                        } else {
                                if (async_event_id ==  NES_AEQE_AEID_LLP_FIN_RECEIVED) {
                                        /* FIN Received but ib state not RTS,
                                                        close complete will be on its way */
-                                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                                       return;
-                               }
-                               spin_unlock_irqrestore(&nesqp->lock, flags);
-                               if (async_event_id == NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE) {
-                                       next_iwarp_state = NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000;
-                                       nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
-                                       nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X,"
-                                                       " also added another reference\n",
-                                                       nesqp->hwqp.qp_id, next_iwarp_state);
-                                       nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
+                                       must_disconn = 0;
                                }
-                               nes_cm_disconn(nesqp);
                        }
-                       break;
-               case NES_AEQE_AEID_LLP_TERMINATE_RECEIVED:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
                        spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TERMINATE_RECEIVED"
-                                       " event on QP%u \n  Q2 Data:\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_FATAL;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
-                       if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) ||
-                                       ((nesqp->ibqp_state == IB_QPS_RTS)&&
-                                       (async_event_id == NES_AEQE_AEID_LLP_CONNECTION_RESET))) {
+
+                       if (must_terminate)
+                               nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
+                       else if (must_disconn) {
+                               if (next_iwarp_state) {
+                                       nes_debug(NES_DBG_AEQ, "issuing hw modifyqp for QP%u. next state = 0x%08X\n",
+                                                 nesqp->hwqp.qp_id, next_iwarp_state);
+                                       nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 0);
+                               }
                                nes_cm_disconn(nesqp);
-                       } else {
-                               nesqp->in_disconnect = 0;
-                               wake_up(&nesqp->kick_waitq);
                        }
                        break;
-               case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_ERROR;
-                       nesqp->hw_tcp_state = NES_AEQE_TCP_STATE_CLOSED;
-                       nesqp->last_aeq = async_event_id;
-                       if (nesqp->cm_id) {
-                               nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES"
-                                               " event on QP%u, remote IP = 0x%08X \n",
-                                               nesqp->hwqp.qp_id,
-                                               ntohl(nesqp->cm_id->remote_addr.sin_addr.s_addr));
-                       } else {
-                               nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_TOO_MANY_RETRIES"
-                                               " event on QP%u \n",
-                                               nesqp->hwqp.qp_id);
-                       }
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR | NES_CQP_QP_RESET;
-                       nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_FATAL;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
+
+               case NES_AEQE_AEID_TERMINATE_SENT:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       nes_terminate_send_fin(nesdev, nesqp, aeqe);
                        break;
-               case NES_AEQE_AEID_AMP_BAD_STAG_INDEX:
-                       if (NES_AEQE_INBOUND_RDMA&aeq_info) {
-                               nesqp = nesadapter->qp_table[le32_to_cpu(
-                                               aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
-                       } else {
-                               /* TODO: get the actual WQE and mask off wqe index */
-                               context &= ~((u64)511);
-                               nesqp = *((struct nes_qp **)&context);
-                       }
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_BAD_STAG_INDEX event on QP%u\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_ACCESS_ERR;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
+
+               case NES_AEQE_AEID_LLP_TERMINATE_RECEIVED:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       nes_terminate_received(nesdev, nesqp, aeqe);
                        break;
+
+               case NES_AEQE_AEID_AMP_BAD_STAG_KEY:
+               case NES_AEQE_AEID_AMP_BAD_STAG_INDEX:
                case NES_AEQE_AEID_AMP_UNALLOCATED_STAG:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_AMP_UNALLOCATED_STAG event on QP%u\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_ACCESS_ERR;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
-                       break;
+               case NES_AEQE_AEID_AMP_INVALID_STAG:
+               case NES_AEQE_AEID_AMP_RIGHTS_VIOLATION:
+               case NES_AEQE_AEID_AMP_INVALIDATE_NO_REMOTE_ACCESS_RIGHTS:
                case NES_AEQE_AEID_PRIV_OPERATION_DENIED:
-                       nesqp = nesadapter->qp_table[le32_to_cpu(aeqe->aeqe_words
-                                       [NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_PRIV_OPERATION_DENIED event on QP%u,"
-                                       " nesqp = %p, AE reported %p\n",
-                                       nesqp->hwqp.qp_id, nesqp, *((struct nes_qp **)&context));
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_ACCESS_ERR;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
+               case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER:
+               case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION:
+               case NES_AEQE_AEID_AMP_TO_WRAP:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_ACCESS_ERR);
+                       break;
+
+               case NES_AEQE_AEID_LLP_SEGMENT_TOO_LARGE:
+               case NES_AEQE_AEID_LLP_SEGMENT_TOO_SMALL:
+               case NES_AEQE_AEID_DDP_UBE_INVALID_MO:
+               case NES_AEQE_AEID_DDP_UBE_INVALID_QN:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       if (iwarp_opcode(nesqp, aeq_info) > IWARP_OPCODE_TERM) {
+                               aeq_info &= 0xffff0000;
+                               aeq_info |= NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE;
+                               aeqe->aeqe_words[NES_AEQE_MISC_IDX] = cpu_to_le32(aeq_info);
                        }
+
+               case NES_AEQE_AEID_RDMAP_ROE_BAD_LLP_CLOSE:
+               case NES_AEQE_AEID_LLP_TOO_MANY_RETRIES:
+               case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE:
+               case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR:
+               case NES_AEQE_AEID_AMP_BAD_QP:
+               case NES_AEQE_AEID_LLP_RECEIVED_MARKER_AND_LENGTH_FIELDS_DONT_MATCH:
+               case NES_AEQE_AEID_DDP_LCE_LOCAL_CATASTROPHIC:
+               case NES_AEQE_AEID_DDP_NO_L_BIT:
+               case NES_AEQE_AEID_DDP_INVALID_MSN_GAP_IN_MSN:
+               case NES_AEQE_AEID_DDP_INVALID_MSN_RANGE_IS_NOT_VALID:
+               case NES_AEQE_AEID_DDP_UBE_INVALID_DDP_VERSION:
+               case NES_AEQE_AEID_RDMAP_ROE_INVALID_RDMAP_VERSION:
+               case NES_AEQE_AEID_RDMAP_ROE_UNEXPECTED_OPCODE:
+               case NES_AEQE_AEID_AMP_BAD_PD:
+               case NES_AEQE_AEID_AMP_FASTREG_SHARED:
+               case NES_AEQE_AEID_AMP_FASTREG_VALID_STAG:
+               case NES_AEQE_AEID_AMP_FASTREG_MW_STAG:
+               case NES_AEQE_AEID_AMP_FASTREG_INVALID_RIGHTS:
+               case NES_AEQE_AEID_AMP_FASTREG_PBL_TABLE_OVERFLOW:
+               case NES_AEQE_AEID_AMP_FASTREG_INVALID_LENGTH:
+               case NES_AEQE_AEID_AMP_INVALIDATE_SHARED:
+               case NES_AEQE_AEID_AMP_INVALIDATE_MR_WITH_BOUND_WINDOWS:
+               case NES_AEQE_AEID_AMP_MWBIND_VALID_STAG:
+               case NES_AEQE_AEID_AMP_MWBIND_OF_MR_STAG:
+               case NES_AEQE_AEID_AMP_MWBIND_TO_ZERO_BASED_STAG:
+               case NES_AEQE_AEID_AMP_MWBIND_TO_MW_STAG:
+               case NES_AEQE_AEID_AMP_MWBIND_INVALID_RIGHTS:
+               case NES_AEQE_AEID_AMP_MWBIND_INVALID_BOUNDS:
+               case NES_AEQE_AEID_AMP_MWBIND_TO_INVALID_PARENT:
+               case NES_AEQE_AEID_AMP_MWBIND_BIND_DISABLED:
+               case NES_AEQE_AEID_BAD_CLOSE:
+               case NES_AEQE_AEID_RDMA_READ_WHILE_ORD_ZERO:
+               case NES_AEQE_AEID_STAG_ZERO_INVALID:
+               case NES_AEQE_AEID_ROE_INVALID_RDMA_READ_REQUEST:
+               case NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP:
+                       nesqp = (struct nes_qp *)(unsigned long)context;
+                       nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);
                        break;
+
                case NES_AEQE_AEID_CQ_OPERATION_ERROR:
                        context <<= 1;
                        nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_CQ_OPERATION_ERROR event on CQ%u, %p\n",
@@ -3167,81 +3503,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
                                }
                        }
                        break;
-               case NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG_FOR_AVAILABLE_BUFFER:
-                       nesqp = nesadapter->qp_table[le32_to_cpu(
-                                       aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX])-NES_FIRST_QPN];
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_DDP_MESSAGE_TOO_LONG"
-                                       "_FOR_AVAILABLE_BUFFER event on QP%u\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_ACCESS_ERR;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
-                       /* tell cm to disconnect, cm will queue work to thread */
-                       nes_cm_disconn(nesqp);
-                       break;
-               case NES_AEQE_AEID_DDP_UBE_INVALID_MSN_NO_BUFFER_AVAILABLE:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_DDP_UBE_INVALID_MSN"
-                                       "_NO_BUFFER_AVAILABLE event on QP%u\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_FATAL;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
-                       /* tell cm to disconnect, cm will queue work to thread */
-                       nes_cm_disconn(nesqp);
-                       break;
-               case NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       nes_debug(NES_DBG_AEQ, "Processing an NES_AEQE_AEID_LLP_RECEIVED_MPA_CRC_ERROR"
-                                       " event on QP%u \n  Q2 Data:\n",
-                                       nesqp->hwqp.qp_id);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_FATAL;
-                               nesqp->ibqp.event_handler(&ibevent, nesqp->ibqp.qp_context);
-                       }
-                       /* tell cm to disconnect, cm will queue work to thread */
-                       nes_cm_disconn(nesqp);
-                       break;
-                       /* TODO: additional AEs need to be here */
-               case NES_AEQE_AEID_AMP_BOUNDS_VIOLATION:
-                       nesqp = *((struct nes_qp **)&context);
-                       spin_lock_irqsave(&nesqp->lock, flags);
-                       nesqp->hw_iwarp_state = iwarp_state;
-                       nesqp->hw_tcp_state = tcp_state;
-                       nesqp->last_aeq = async_event_id;
-                       spin_unlock_irqrestore(&nesqp->lock, flags);
-                       if (nesqp->ibqp.event_handler) {
-                               ibevent.device = nesqp->ibqp.device;
-                               ibevent.element.qp = &nesqp->ibqp;
-                               ibevent.event = IB_EVENT_QP_ACCESS_ERR;
-                               nesqp->ibqp.event_handler(&ibevent,
-                                               nesqp->ibqp.qp_context);
-                       }
-                       nes_cm_disconn(nesqp);
-                       break;
+
                default:
                        nes_debug(NES_DBG_AEQ, "Processing an iWARP related AE for QP, misc = 0x%04X\n",
                                        async_event_id);
@@ -3250,7 +3512,6 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,
 
 }
 
-
 /**
  * nes_iwarp_ce_handler
  */
index c3654c6..4a0bfcd 100644 (file)
@@ -241,6 +241,7 @@ enum nes_cqp_stag_wqeword_idx {
 };
 
 #define NES_CQP_OP_IWARP_STATE_SHIFT 28
+#define NES_CQP_OP_TERMLEN_SHIFT     28
 
 enum nes_cqp_qp_bits {
        NES_CQP_QP_ARP_VALID = (1<<8),
@@ -265,6 +266,8 @@ enum nes_cqp_qp_bits {
        NES_CQP_QP_IWARP_STATE_TERMINATE = (5<<NES_CQP_OP_IWARP_STATE_SHIFT),
        NES_CQP_QP_IWARP_STATE_ERROR = (6<<NES_CQP_OP_IWARP_STATE_SHIFT),
        NES_CQP_QP_IWARP_STATE_MASK = (7<<NES_CQP_OP_IWARP_STATE_SHIFT),
+       NES_CQP_QP_TERM_DONT_SEND_FIN = (1<<24),
+       NES_CQP_QP_TERM_DONT_SEND_TERM_MSG = (1<<25),
        NES_CQP_QP_RESET = (1<<31),
 };
 
@@ -633,11 +636,14 @@ enum nes_aeqe_bits {
        NES_AEQE_INBOUND_RDMA = (1<<19),
        NES_AEQE_IWARP_STATE_MASK = (7<<20),
        NES_AEQE_TCP_STATE_MASK = (0xf<<24),
+       NES_AEQE_Q2_DATA_WRITTEN = (0x3<<28),
        NES_AEQE_VALID = (1<<31),
 };
 
 #define NES_AEQE_IWARP_STATE_SHIFT     20
 #define NES_AEQE_TCP_STATE_SHIFT       24
+#define NES_AEQE_Q2_DATA_ETHERNET       (1<<28)
+#define NES_AEQE_Q2_DATA_MPA            (1<<29)
 
 enum nes_aeqe_iwarp_state {
        NES_AEQE_IWARP_STATE_NON_EXISTANT = 0,
@@ -1119,6 +1125,7 @@ struct nes_adapter {
        u8            netdev_max;       /* from host nic address count in EEPROM */
        u8            port_count;
        u8            virtwq;
+       u8            send_term_ok;
        u8            et_use_adaptive_rx_coalesce;
        u8            adapter_fcn_count;
        u8 pft_mcast_map[NES_PFT_SIZE];
@@ -1217,6 +1224,90 @@ struct nes_ib_device {
        u32 num_pd;
 };
 
+enum nes_hdrct_flags {
+       DDP_LEN_FLAG                    = 0x80,
+       DDP_HDR_FLAG                    = 0x40,
+       RDMA_HDR_FLAG                   = 0x20
+};
+
+enum nes_term_layers {
+       LAYER_RDMA                      = 0,
+       LAYER_DDP                       = 1,
+       LAYER_MPA                       = 2
+};
+
+enum nes_term_error_types {
+       RDMAP_CATASTROPHIC              = 0,
+       RDMAP_REMOTE_PROT               = 1,
+       RDMAP_REMOTE_OP                 = 2,
+       DDP_CATASTROPHIC                = 0,
+       DDP_TAGGED_BUFFER               = 1,
+       DDP_UNTAGGED_BUFFER             = 2,
+       DDP_LLP                         = 3
+};
+
+enum nes_term_rdma_errors {
+       RDMAP_INV_STAG                  = 0x00,
+       RDMAP_INV_BOUNDS                = 0x01,
+       RDMAP_ACCESS                    = 0x02,
+       RDMAP_UNASSOC_STAG              = 0x03,
+       RDMAP_TO_WRAP                   = 0x04,
+       RDMAP_INV_RDMAP_VER             = 0x05,
+       RDMAP_UNEXPECTED_OP             = 0x06,
+       RDMAP_CATASTROPHIC_LOCAL        = 0x07,
+       RDMAP_CATASTROPHIC_GLOBAL       = 0x08,
+       RDMAP_CANT_INV_STAG             = 0x09,
+       RDMAP_UNSPECIFIED               = 0xff
+};
+
+enum nes_term_ddp_errors {
+       DDP_CATASTROPHIC_LOCAL          = 0x00,
+       DDP_TAGGED_INV_STAG             = 0x00,
+       DDP_TAGGED_BOUNDS               = 0x01,
+       DDP_TAGGED_UNASSOC_STAG         = 0x02,
+       DDP_TAGGED_TO_WRAP              = 0x03,
+       DDP_TAGGED_INV_DDP_VER          = 0x04,
+       DDP_UNTAGGED_INV_QN             = 0x01,
+       DDP_UNTAGGED_INV_MSN_NO_BUF     = 0x02,
+       DDP_UNTAGGED_INV_MSN_RANGE      = 0x03,
+       DDP_UNTAGGED_INV_MO             = 0x04,
+       DDP_UNTAGGED_INV_TOO_LONG       = 0x05,
+       DDP_UNTAGGED_INV_DDP_VER        = 0x06
+};
+
+enum nes_term_mpa_errors {
+       MPA_CLOSED                      = 0x01,
+       MPA_CRC                         = 0x02,
+       MPA_MARKER                      = 0x03,
+       MPA_REQ_RSP                     = 0x04,
+};
+
+struct nes_terminate_hdr {
+       u8 layer_etype;
+       u8 error_code;
+       u8 hdrct;
+       u8 rsvd;
+};
+
+/* Used to determine how to fill in terminate error codes */
+#define IWARP_OPCODE_WRITE             0
+#define IWARP_OPCODE_READREQ           1
+#define IWARP_OPCODE_READRSP           2
+#define IWARP_OPCODE_SEND              3
+#define IWARP_OPCODE_SEND_INV          4
+#define IWARP_OPCODE_SEND_SE           5
+#define IWARP_OPCODE_SEND_SE_INV       6
+#define IWARP_OPCODE_TERM              7
+
+/* These values are used only during terminate processing */
+#define TERM_DDP_LEN_TAGGED    14
+#define TERM_DDP_LEN_UNTAGGED  18
+#define TERM_RDMA_LEN          28
+#define RDMA_OPCODE_MASK       0x0f
+#define RDMA_READ_REQ_OPCODE   1
+#define BAD_FRAME_OFFSET       64
+#define CQE_MAJOR_DRV          0x8000
+
 #define nes_vlan_rx vlan_hwaccel_receive_skb
 #define nes_netif_rx netif_receive_skb
 
index b34072b..9687c39 100644 (file)
@@ -183,6 +183,9 @@ int nes_read_eeprom_values(struct nes_device *nesdev, struct nes_adapter *nesada
                } else if (((major_ver == 2) && (minor_ver > 21)) || ((major_ver > 2) && (major_ver != 255))) {
                        nesadapter->virtwq = 1;
                }
+               if (((major_ver == 3) && (minor_ver >= 16)) || (major_ver > 3))
+                       nesadapter->send_term_ok = 1;
+
                nesadapter->firmware_version = (((u32)(u8)(eeprom_data>>8))  <<  16) +
                                (u32)((u8)eeprom_data);
 
index c6b5873..36666ac 100644 (file)
@@ -2923,7 +2923,7 @@ static int nes_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
  * nes_hw_modify_qp
  */
 int nes_hw_modify_qp(struct nes_device *nesdev, struct nes_qp *nesqp,
-               u32 next_iwarp_state, u32 wait_completion)
+               u32 next_iwarp_state, u32 termlen, u32 wait_completion)
 {
        struct nes_hw_cqp_wqe *cqp_wqe;
        /* struct iw_cm_id *cm_id = nesqp->cm_id; */
@@ -2955,6 +2955,13 @@ int nes_hw_modify_qp(struct nes_device *nesdev, struct nes_qp *nesqp,
        set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_WQE_ID_IDX, nesqp->hwqp.qp_id);
        set_wqe_64bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_CONTEXT_LOW_IDX, (u64)nesqp->nesqp_context_pbase);
 
+       /* If sending a terminate message, fill in the length (in words) */
+       if (((next_iwarp_state & NES_CQP_QP_IWARP_STATE_MASK) == NES_CQP_QP_IWARP_STATE_TERMINATE) &&
+           !(next_iwarp_state & NES_CQP_QP_TERM_DONT_SEND_TERM_MSG)) {
+               termlen = ((termlen + 3) >> 2) << NES_CQP_OP_TERMLEN_SHIFT;
+               set_wqe_32bit_value(cqp_wqe->wqe_words, NES_CQP_QP_WQE_NEW_MSS_IDX, termlen);
+       }
+
        atomic_set(&cqp_request->refcount, 2);
        nes_post_cqp_request(nesdev, cqp_request);
 
@@ -3125,6 +3132,9 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                }
                                nes_debug(NES_DBG_MOD_QP, "QP%u: new state = error\n",
                                                nesqp->hwqp.qp_id);
+                               if (nesqp->term_flags)
+                                       del_timer(&nesqp->terminate_timer);
+
                                next_iwarp_state = NES_CQP_QP_IWARP_STATE_ERROR;
                                /* next_iwarp_state = (NES_CQP_QP_IWARP_STATE_TERMINATE | 0x02000000); */
                                        if (nesqp->hte_added) {
@@ -3202,7 +3212,7 @@ int nes_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
 
        if (issue_modify_qp) {
                nes_debug(NES_DBG_MOD_QP, "call nes_hw_modify_qp\n");
-               ret = nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 1);
+               ret = nes_hw_modify_qp(nesdev, nesqp, next_iwarp_state, 0, 1);
                if (ret)
                        nes_debug(NES_DBG_MOD_QP, "nes_hw_modify_qp (next_iwarp_state = 0x%08X)"
                                        " failed for QP%u.\n",
@@ -3367,6 +3377,12 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
        head = nesqp->hwqp.sq_head;
 
        while (ib_wr) {
+               /* Check for QP error */
+               if (nesqp->term_flags) {
+                       err = -EINVAL;
+                       break;
+               }
+
                /* Check for SQ overflow */
                if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) {
                        err = -EINVAL;
@@ -3523,6 +3539,12 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
        head = nesqp->hwqp.rq_head;
 
        while (ib_wr) {
+               /* Check for QP error */
+               if (nesqp->term_flags) {
+                       err = -EINVAL;
+                       break;
+               }
+
                if (ib_wr->num_sge > nesdev->nesadapter->max_sge) {
                        err = -EINVAL;
                        break;
index 7df34fe..d92b1ef 100644 (file)
@@ -40,6 +40,10 @@ struct nes_device;
 #define NES_MAX_USER_DB_REGIONS  4096
 #define NES_MAX_USER_WQ_REGIONS  4096
 
+#define NES_TERM_SENT            0x01
+#define NES_TERM_RCVD            0x02
+#define NES_TERM_DONE            0x04
+
 struct nes_ucontext {
        struct ib_ucontext ibucontext;
        struct nes_device  *nesdev;
@@ -159,6 +163,8 @@ struct nes_qp {
        void                  *pbl_vbase;
        dma_addr_t            pbl_pbase;
        struct page           *page;
+       struct timer_list     terminate_timer;
+       enum ib_event_type    terminate_eventtype;
        wait_queue_head_t     kick_waitq;
        u16                   in_disconnect;
        u16                   private_data_len;
@@ -169,6 +175,7 @@ struct nes_qp {
        u8                    hw_iwarp_state;
        u8                    flush_issued;
        u8                    hw_tcp_state;
+       u8                    term_flags;
        u8                    destroyed;
 };
 #endif                 /* NES_VERBS_H */