diff options
Diffstat (limited to 'drivers/infiniband/hw/nes/nes_hw.c')
| -rw-r--r-- | drivers/infiniband/hw/nes/nes_hw.c | 245 | 
1 files changed, 182 insertions, 63 deletions
diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 1980a461c49..90200245c5e 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 2006 - 2009 Intel Corporation.  All rights reserved. + * Copyright (c) 2006 - 2011 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 @@ -75,12 +75,11 @@ 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[] = { -	"Non-Existant", +	"Non-Existent",  	"Idle",  	"RTS",  	"Closing", @@ -91,7 +90,7 @@ static unsigned char *nes_iwarp_state_str[] = {  };  static unsigned char *nes_tcp_state_str[] = { -	"Non-Existant", +	"Non-Existent",  	"Closed",  	"Listen",  	"SYN Sent", @@ -110,6 +109,14 @@ static unsigned char *nes_tcp_state_str[] = {  };  #endif +static inline void print_ip(struct nes_cm_node *cm_node) +{ +	unsigned char *rem_addr; +	if (cm_node) { +		rem_addr = (unsigned char *)&cm_node->rem_addr; +		printk(KERN_ERR PFX "Remote IP addr: %pI4\n", rem_addr); +	} +}  /**   * nes_nic_init_timer_defaults @@ -1521,7 +1528,7 @@ int nes_init_phy(struct nes_device *nesdev)  	} else {  		/* setup 10G MDIO operation */  		tx_config &= 0xFFFFFFE3; -		tx_config |= 0x15; +		tx_config |= 0x1D;  	}  	nes_write_indexed(nesdev, NES_IDX_MAC_TX_CONFIG, tx_config); @@ -1555,6 +1562,7 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)  	struct nes_hw_nic_rq_wqe *nic_rqe;  	struct nes_hw_nic *nesnic;  	struct nes_device *nesdev; +	struct nes_rskb_cb *cb;  	u32 rx_wqes_posted = 0;  	nesnic = &nesvnic->nic; @@ -1580,6 +1588,9 @@ static void nes_replenish_nic_rq(struct nes_vnic *nesvnic)  			bus_address = pci_map_single(nesdev->pcidev,  					skb->data, nesvnic->max_frame_size, PCI_DMA_FROMDEVICE); +			cb = (struct nes_rskb_cb *)&skb->cb[0]; +			cb->busaddr = bus_address; +			cb->maplen = nesvnic->max_frame_size;  			nic_rqe = &nesnic->rq_vbase[nesvnic->nic.rq_head];  			nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = @@ -1669,6 +1680,7 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)  	u32 cqp_head;  	u32 counter;  	u32 wqe_count; +	struct nes_rskb_cb *cb;  	u8 jumbomode=0;  	/* Allocate fragment, SQ, RQ, and CQ; Reuse CEQ based on the PCI function */ @@ -1845,6 +1857,9 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)  		pmem = pci_map_single(nesdev->pcidev, skb->data,  				nesvnic->max_frame_size, PCI_DMA_FROMDEVICE); +		cb = (struct nes_rskb_cb *)&skb->cb[0]; +		cb->busaddr = pmem; +		cb->maplen = nesvnic->max_frame_size;  		nic_rqe = &nesvnic->nic.rq_vbase[counter];  		nic_rqe->wqe_words[NES_NIC_RQ_WQE_LENGTH_1_0_IDX] = cpu_to_le32(nesvnic->max_frame_size); @@ -1873,6 +1888,13 @@ int nes_init_nic_qp(struct nes_device *nesdev, struct net_device *netdev)  			jumbomode = 1;  		nes_nic_init_timer_defaults(nesdev, jumbomode);  	} +	if ((nesdev->nesadapter->allow_unaligned_fpdus) && +		(nes_init_mgt_qp(nesdev, netdev, nesvnic))) { +			nes_debug(NES_DBG_INIT, "%s: Out of memory for pau nic\n", netdev->name); +			nes_destroy_nic_qp(nesvnic); +		return -ENOMEM; +	} +  	nesvnic->lro_mgr.max_aggr       = nes_lro_max_aggr;  	nesvnic->lro_mgr.max_desc       = NES_MAX_LRO_DESCRIPTORS;  	nesvnic->lro_mgr.lro_arr        = nesvnic->lro_desc; @@ -1895,28 +1917,29 @@ void nes_destroy_nic_qp(struct nes_vnic *nesvnic)  	struct nes_device *nesdev = nesvnic->nesdev;  	struct nes_hw_cqp_wqe *cqp_wqe;  	struct nes_hw_nic_sq_wqe *nic_sqe; -	struct nes_hw_nic_rq_wqe *nic_rqe;  	__le16 *wqe_fragment_length;  	u16  wqe_fragment_index; -	u64 wqe_frag;  	u32 cqp_head;  	u32 wqm_cfg0;  	unsigned long flags; +	struct sk_buff *rx_skb; +	struct nes_rskb_cb *cb;  	int ret; +	if (nesdev->nesadapter->allow_unaligned_fpdus) +		nes_destroy_mgt(nesvnic); +  	/* clear wqe stall before destroying NIC QP */  	wqm_cfg0 = nes_read_indexed(nesdev, NES_IDX_WQM_CONFIG0);  	nes_write_indexed(nesdev, NES_IDX_WQM_CONFIG0, wqm_cfg0 & 0xFFFF7FFF);  	/* Free remaining NIC receive buffers */  	while (nesvnic->nic.rq_head != nesvnic->nic.rq_tail) { -		nic_rqe   = &nesvnic->nic.rq_vbase[nesvnic->nic.rq_tail]; -		wqe_frag  = (u64)le32_to_cpu( -			nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX]); -		wqe_frag |= ((u64)le32_to_cpu( -			nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX]))<<32; -		pci_unmap_single(nesdev->pcidev, (dma_addr_t)wqe_frag, -				nesvnic->max_frame_size, PCI_DMA_FROMDEVICE); +		rx_skb = nesvnic->nic.rx_skb[nesvnic->nic.rq_tail]; +		cb = (struct nes_rskb_cb *)&rx_skb->cb[0]; +		pci_unmap_single(nesdev->pcidev, cb->busaddr, cb->maplen, +			PCI_DMA_FROMDEVICE); +  		dev_kfree_skb(nesvnic->nic.rx_skb[nesvnic->nic.rq_tail++]);  		nesvnic->nic.rq_tail &= (nesvnic->nic.rq_size - 1);  	} @@ -2608,6 +2631,15 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)  						netif_start_queue(nesvnic->netdev);  					nesvnic->linkup = 1;  					netif_carrier_on(nesvnic->netdev); + +					spin_lock(&nesvnic->port_ibevent_lock); +					if (nesvnic->of_device_registered) { +						if (nesdev->iw_status == 0) { +							nesdev->iw_status = 1; +							nes_port_ibevent(nesvnic); +						} +					} +					spin_unlock(&nesvnic->port_ibevent_lock);  				}  			}  		} else { @@ -2633,9 +2665,23 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)  						netif_stop_queue(nesvnic->netdev);  					nesvnic->linkup = 0;  					netif_carrier_off(nesvnic->netdev); + +					spin_lock(&nesvnic->port_ibevent_lock); +					if (nesvnic->of_device_registered) { +						if (nesdev->iw_status == 1) { +							nesdev->iw_status = 0; +							nes_port_ibevent(nesvnic); +						} +					} +					spin_unlock(&nesvnic->port_ibevent_lock);  				}  			}  		} +		if (nesadapter->phy_type[mac_index] == NES_PHY_TYPE_SFP_D) { +			nesdev->link_recheck = 1; +			mod_delayed_work(system_wq, &nesdev->work, +					 NES_LINK_RECHECK_DELAY); +		}  	}  	spin_unlock_irqrestore(&nesadapter->phy_lock, flags); @@ -2643,6 +2689,84 @@ static void nes_process_mac_intr(struct nes_device *nesdev, u32 mac_number)  	nesadapter->mac_sw_state[mac_number] = NES_MAC_SW_IDLE;  } +void nes_recheck_link_status(struct work_struct *work) +{ +	unsigned long flags; +	struct nes_device *nesdev = container_of(work, struct nes_device, work.work); +	struct nes_adapter *nesadapter = nesdev->nesadapter; +	struct nes_vnic *nesvnic; +	u32 mac_index = nesdev->mac_index; +	u16 phy_data; +	u16 temp_phy_data; + +	spin_lock_irqsave(&nesadapter->phy_lock, flags); + +	/* check link status */ +	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 1, 0x9003); +	temp_phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); + +	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021); +	nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); +	nes_read_10G_phy_reg(nesdev, nesadapter->phy_index[mac_index], 3, 0x0021); +	phy_data = (u16)nes_read_indexed(nesdev, NES_IDX_MAC_MDIO_CONTROL); + +	phy_data = (!temp_phy_data && (phy_data == 0x8000)) ? 0x4 : 0x0; + +	nes_debug(NES_DBG_PHY, "%s: Phy data = 0x%04X, link was %s.\n", +		__func__, phy_data, +		nesadapter->mac_link_down[mac_index] ? "DOWN" : "UP"); + +	if (phy_data & 0x0004) { +		nesadapter->mac_link_down[mac_index] = 0; +		list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) { +			if (nesvnic->linkup == 0) { +				printk(PFX "The Link is now up for port %s, netdev %p.\n", +						nesvnic->netdev->name, nesvnic->netdev); +				if (netif_queue_stopped(nesvnic->netdev)) +					netif_start_queue(nesvnic->netdev); +				nesvnic->linkup = 1; +				netif_carrier_on(nesvnic->netdev); + +				spin_lock(&nesvnic->port_ibevent_lock); +				if (nesvnic->of_device_registered) { +					if (nesdev->iw_status == 0) { +						nesdev->iw_status = 1; +						nes_port_ibevent(nesvnic); +					} +				} +				spin_unlock(&nesvnic->port_ibevent_lock); +			} +		} + +	} else { +		nesadapter->mac_link_down[mac_index] = 1; +		list_for_each_entry(nesvnic, &nesadapter->nesvnic_list[mac_index], list) { +			if (nesvnic->linkup == 1) { +				printk(PFX "The Link is now down for port %s, netdev %p.\n", +						nesvnic->netdev->name, nesvnic->netdev); +				if (!(netif_queue_stopped(nesvnic->netdev))) +					netif_stop_queue(nesvnic->netdev); +				nesvnic->linkup = 0; +				netif_carrier_off(nesvnic->netdev); + +				spin_lock(&nesvnic->port_ibevent_lock); +				if (nesvnic->of_device_registered) { +					if (nesdev->iw_status == 1) { +						nesdev->iw_status = 0; +						nes_port_ibevent(nesvnic); +					} +				} +				spin_unlock(&nesvnic->port_ibevent_lock); +			} +		} +	} +	if (nesdev->link_recheck++ < NES_LINK_RECHECK_MAX) +		schedule_delayed_work(&nesdev->work, NES_LINK_RECHECK_DELAY); +	else +		nesdev->link_recheck = 0; + +	spin_unlock_irqrestore(&nesadapter->phy_lock, flags); +}  static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq) @@ -2672,6 +2796,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)  	struct nes_hw_nic_sq_wqe *nic_sqe;  	struct sk_buff *skb;  	struct sk_buff *rx_skb; +	struct nes_rskb_cb *cb;  	__le16 *wqe_fragment_length;  	u32 head;  	u32 cq_size; @@ -2756,6 +2881,8 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)  				bus_address += ((u64)le32_to_cpu(nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])) << 32;  				pci_unmap_single(nesdev->pcidev, bus_address,  						nesvnic->max_frame_size, PCI_DMA_FROMDEVICE); +				cb = (struct nes_rskb_cb *)&rx_skb->cb[0]; +				cb->busaddr = 0;  				/* rx_skb->tail = rx_skb->data + rx_pkt_size; */  				/* rx_skb->len = rx_pkt_size; */  				rx_skb->len = 0;  /* TODO: see if this is necessary */ @@ -2782,9 +2909,8 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)  					if ((cqe_errv &  							(NES_NIC_ERRV_BITS_IPV4_CSUM_ERR | NES_NIC_ERRV_BITS_TCPUDP_CSUM_ERR |  							NES_NIC_ERRV_BITS_IPH_ERR | NES_NIC_ERRV_BITS_WQE_OVERRUN)) == 0) { -						if (nesvnic->rx_checksum_disabled == 0) { +						if (nesvnic->netdev->features & NETIF_F_RXCSUM)  							rx_skb->ip_summed = CHECKSUM_UNNECESSARY; -						}  					} else  						nes_debug(NES_DBG_CQ, "%s: unsuccessfully checksummed TCP or UDP packet."  								" errv = 0x%X, pkt_type = 0x%X.\n", @@ -2794,7 +2920,7 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)  					if ((cqe_errv &  							(NES_NIC_ERRV_BITS_IPV4_CSUM_ERR | NES_NIC_ERRV_BITS_IPH_ERR |  							NES_NIC_ERRV_BITS_WQE_OVERRUN)) == 0) { -						if (nesvnic->rx_checksum_disabled == 0) { +						if (nesvnic->netdev->features & NETIF_F_RXCSUM) {  							rx_skb->ip_summed = CHECKSUM_UNNECESSARY;  							/* nes_debug(NES_DBG_CQ, "%s: Reporting successfully checksummed IPv4 packet.\n",  								  nesvnic->netdev->name); */ @@ -2815,24 +2941,19 @@ void nes_nic_ce_handler(struct nes_device *nesdev, struct nes_hw_nic_cq *cq)  					goto skip_rx_indicate0; -				if ((cqe_misc & NES_NIC_CQE_TAG_VALID) && -				    (nesvnic->vlan_grp != NULL)) { +				if (cqe_misc & NES_NIC_CQE_TAG_VALID) {  					vlan_tag = (u16)(le32_to_cpu(  							cq->cq_vbase[head].cqe_words[NES_NIC_CQE_TAG_PKT_TYPE_IDX])  							>> 16);  					nes_debug(NES_DBG_CQ, "%s: Reporting stripped VLAN packet. Tag = 0x%04X\n",  							nesvnic->netdev->name, vlan_tag); -					if (nes_use_lro) -						lro_vlan_hwaccel_receive_skb(&nesvnic->lro_mgr, rx_skb, -								nesvnic->vlan_grp, vlan_tag, NULL); -					else -						nes_vlan_rx(rx_skb, nesvnic->vlan_grp, vlan_tag); -				} else { -					if (nes_use_lro) -						lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL); -					else -						nes_netif_rx(rx_skb); + +					__vlan_hwaccel_put_tag(rx_skb, htons(ETH_P_8021Q), vlan_tag);  				} +				if (nes_use_lro) +					lro_receive_skb(&nesvnic->lro_mgr, rx_skb, NULL); +				else +					netif_receive_skb(rx_skb);  skip_rx_indicate0:  				; @@ -2886,6 +3007,7 @@ skip_rx_indicate0:  } +  /**   * nes_cqp_ce_handler   */ @@ -2900,6 +3022,8 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)  	u32 cq_size;  	u32 cqe_count=0;  	u32 error_code; +	u32 opcode; +	u32 ctx_index;  	/* u32 counter; */  	head = cq->cq_head; @@ -2910,12 +3034,9 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)  		/* nes_debug(NES_DBG_CQP, "head=%u cqe_words=%08X\n", head,  			  le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])); */ -		if (le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]) & NES_CQE_VALID) { -			u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head]. -					cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) | -					((u64)(le32_to_cpu(cq->cq_vbase[head]. -					cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]))); -			cqp = *((struct nes_hw_cqp **)&u64temp); +		opcode = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX]); +		if (opcode & NES_CQE_VALID) { +			cqp = &nesdev->cqp;  			error_code = le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_ERROR_CODE_IDX]);  			if (error_code) { @@ -2924,15 +3045,14 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)  						le32_to_cpu(cq->cq_vbase[head].cqe_words[NES_CQE_OPCODE_IDX])&0x3f,  						(u16)(error_code >> 16),  						(u16)error_code); -				nes_debug(NES_DBG_CQP, "cqp: qp_id=%u, sq_head=%u, sq_tail=%u\n", -						cqp->qp_id, cqp->sq_head, cqp->sq_tail);  			} -			u64temp = (((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail]. -					wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX]))) << 32) | -					((u64)(le32_to_cpu(nesdev->cqp.sq_vbase[cqp->sq_tail]. -					wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX]))); -			cqp_request = *((struct nes_cqp_request **)&u64temp); +			u64temp = (((u64)(le32_to_cpu(cq->cq_vbase[head]. +					cqe_words[NES_CQE_COMP_COMP_CTX_HIGH_IDX]))) << 32) | +					((u64)(le32_to_cpu(cq->cq_vbase[head]. +					cqe_words[NES_CQE_COMP_COMP_CTX_LOW_IDX]))); + +			cqp_request = (struct nes_cqp_request *)(unsigned long)u64temp;  			if (cqp_request) {  				if (cqp_request->waiting) {  					/* nes_debug(NES_DBG_CQP, "%s: Waking up requestor\n"); */ @@ -2978,9 +3098,15 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)  		cqp_wqe = &nesdev->cqp.sq_vbase[head];  		memcpy(cqp_wqe, &cqp_request->cqp_wqe, sizeof(*cqp_wqe));  		barrier(); -		cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] = + +		opcode = cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX]; +		if ((opcode & NES_CQP_OPCODE_MASK) == NES_CQP_DOWNLOAD_SEGMENT) +			ctx_index = NES_CQP_WQE_DL_COMP_CTX_LOW_IDX; +		else +			ctx_index = NES_CQP_WQE_COMP_CTX_LOW_IDX; +		cqp_wqe->wqe_words[ctx_index] =  			cpu_to_le32((u32)((unsigned long)cqp_request)); -		cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = +		cqp_wqe->wqe_words[ctx_index + 1] =  			cpu_to_le32((u32)(upper_32_bits((unsigned long)cqp_request)));  		nes_debug(NES_DBG_CQP, "CQP request %p (opcode 0x%02X) put on CQPs SQ wqe%u.\n",  				cqp_request, le32_to_cpu(cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX])&0x3f, head); @@ -2996,7 +3122,6 @@ static void nes_cqp_ce_handler(struct nes_device *nesdev, struct nes_hw_cq *cq)  	nes_read32(nesdev->regs+NES_CQE_ALLOC);  } -  static u8 *locate_mpa(u8 *pkt, u32 aeq_info)  {  	if (aeq_info & NES_AEQE_Q2_DATA_ETHERNET) { @@ -3394,7 +3519,7 @@ static void nes_terminate_received(struct nes_device *nesdev,  }  /* Timeout routine in case terminate fails to complete */ -static void nes_terminate_timeout(unsigned long context) +void nes_terminate_timeout(unsigned long context)  {  	struct nes_qp *nesqp = (struct nes_qp *)(unsigned long)context; @@ -3404,11 +3529,7 @@ static void nes_terminate_timeout(unsigned long context)  /* 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); +	mod_timer(&nesqp->terminate_timer, (jiffies + HZ));  }  /** @@ -3456,9 +3577,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,  	aeqe_cq_id = le32_to_cpu(aeqe->aeqe_words[NES_AEQE_COMP_QP_CQ_ID_IDX]);  	if (aeq_info & NES_AEQE_QP) { -		if ((!nes_is_resource_allocated(nesadapter, nesadapter->allocated_qps, -				aeqe_cq_id)) || -				(atomic_read(&nesqp->close_timer_started))) +		if (!nes_is_resource_allocated(nesadapter, +				nesadapter->allocated_qps, +				aeqe_cq_id))  			return;  	} @@ -3469,8 +3590,7 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,  			if (atomic_inc_return(&nesqp->close_timer_started) == 1) {  				if ((tcp_state == NES_AEQE_TCP_STATE_CLOSE_WAIT) && -					(nesqp->ibqp_state == IB_QPS_RTS) && -					((nesadapter->eeprom_version >> 16) != NES_A0)) { +					(nesqp->ibqp_state == IB_QPS_RTS)) {  					spin_lock_irqsave(&nesqp->lock, flags);  					nesqp->hw_iwarp_state = iwarp_state;  					nesqp->hw_tcp_state = tcp_state; @@ -3492,14 +3612,11 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,  			}  			break;  		case NES_AEQE_AEID_LLP_CLOSE_COMPLETE: -			if (nesqp->term_flags) { -				nes_terminate_done(nesqp, 0); -				return; -			}  			spin_lock_irqsave(&nesqp->lock, flags); -			nesqp->hw_iwarp_state = NES_AEQE_IWARP_STATE_CLOSING; +			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_hw_modify_qp(nesdev, nesqp, NES_CQP_QP_IWARP_STATE_CLOSING, 0, 0);  			nes_cm_disconn(nesqp);  			break; @@ -3597,7 +3714,9 @@ static void nes_process_iwarp_aeqe(struct nes_device *nesdev,  		case NES_AEQE_AEID_ROE_INVALID_RDMA_WRITE_OR_READ_RESP:  			printk(KERN_ERR PFX "QP[%u] async_event_id=0x%04X IB_EVENT_QP_FATAL\n",  					nesqp->hwqp.qp_id, async_event_id); -			nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL); +			print_ip(nesqp->cm_node); +			if (!atomic_read(&nesqp->close_timer_started)) +				nes_terminate_connection(nesdev, nesqp, aeqe, IB_EVENT_QP_FATAL);  			break;  		case NES_AEQE_AEID_CQ_OPERATION_ERROR:  | 
