| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:32 -07:00
										 |  |  |  * Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  * Copyright (c) 2005, 2006 PathScale, Inc. 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 | 
					
						
							|  |  |  |  * General Public License (GPL) Version 2, available from the file | 
					
						
							|  |  |  |  * COPYING in the main directory of this source tree, or the | 
					
						
							|  |  |  |  * OpenIB.org BSD license below: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     Redistribution and use in source and binary forms, with or | 
					
						
							|  |  |  |  *     without modification, are permitted provided that the following | 
					
						
							|  |  |  |  *     conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *      - Redistributions of source code must retain the above | 
					
						
							|  |  |  |  *        copyright notice, this list of conditions and the following | 
					
						
							|  |  |  |  *        disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *      - Redistributions in binary form must reproduce the above | 
					
						
							|  |  |  |  *        copyright notice, this list of conditions and the following | 
					
						
							|  |  |  |  *        disclaimer in the documentation and/or other materials | 
					
						
							|  |  |  |  *        provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
					
						
							|  |  |  |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
					
						
							|  |  |  |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
					
						
							|  |  |  |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
					
						
							|  |  |  |  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
					
						
							|  |  |  |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
					
						
							|  |  |  |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
					
						
							|  |  |  |  * SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | #include "ipath_verbs.h"
 | 
					
						
							| 
									
										
										
										
											2006-08-25 11:24:32 -07:00
										 |  |  | #include "ipath_kernel.h"
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* cut down ridiculously long IB macro names */ | 
					
						
							|  |  |  | #define OP(x) IB_OPCODE_RC_##x
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe, | 
					
						
							|  |  |  | 		       u32 psn, u32 pmtu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu; | 
					
						
							|  |  |  | 	ss->sge = wqe->sg_list[0]; | 
					
						
							|  |  |  | 	ss->sg_list = wqe->sg_list + 1; | 
					
						
							|  |  |  | 	ss->num_sge = wqe->wr.num_sge; | 
					
						
							|  |  |  | 	ipath_skip_sge(ss, len); | 
					
						
							|  |  |  | 	return wqe->length - len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_init_restart- initialize the qp->s_sge after a restart | 
					
						
							|  |  |  |  * @qp: the QP who's SGE we're restarting | 
					
						
							|  |  |  |  * @wqe: the work queue to initialize the QP's SGE from | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  |  * The QP s_lock should be held and interrupts disabled. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  */ | 
					
						
							|  |  |  | static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ipath_ibdev *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn, | 
					
						
							|  |  |  | 				ib_mtu_enum_to_int(qp->path_mtu)); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	dev = to_idev(qp->ibqp.device); | 
					
						
							|  |  |  | 	spin_lock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2006-05-23 11:32:32 -07:00
										 |  |  | 	if (list_empty(&qp->timerwait)) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		list_add_tail(&qp->timerwait, | 
					
						
							|  |  |  | 			      &dev->pending[dev->pending_index]); | 
					
						
							|  |  |  | 	spin_unlock(&dev->pending_lock); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_make_rc_ack - construct a response packet (ACK, NAK, or RDMA read) | 
					
						
							|  |  |  |  * @qp: a pointer to the QP | 
					
						
							|  |  |  |  * @ohdr: a pointer to the IB header being constructed | 
					
						
							|  |  |  |  * @pmtu: the path MTU | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  |  * Return 1 if constructed; otherwise, return 0. | 
					
						
							|  |  |  |  * Note that we are in the responder's side of the QP context. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  * Note the QP s_lock must be held. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | static int ipath_make_rc_ack(struct ipath_ibdev *dev, struct ipath_qp *qp, | 
					
						
							|  |  |  | 			     struct ipath_other_headers *ohdr, u32 pmtu) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	struct ipath_ack_entry *e; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	u32 hwords; | 
					
						
							|  |  |  | 	u32 len; | 
					
						
							|  |  |  | 	u32 bth0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	u32 bth2; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	/* Don't send an ACK if we aren't supposed to. */ | 
					
						
							|  |  |  | 	if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* header size in 32-bit words LRH+BTH = (8+12)/4. */ | 
					
						
							|  |  |  | 	hwords = 5; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (qp->s_ack_state) { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	case OP(RDMA_READ_RESPONSE_LAST): | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_ONLY): | 
					
						
							|  |  |  | 	case OP(ATOMIC_ACKNOWLEDGE): | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * We can increment the tail pointer now that the last | 
					
						
							|  |  |  | 		 * response has been sent instead of only being | 
					
						
							|  |  |  | 		 * constructed. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC) | 
					
						
							|  |  |  | 			qp->s_tail_ack_queue = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		/* FALLTHROUGH */ | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 	case OP(SEND_ONLY): | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	case OP(ACKNOWLEDGE): | 
					
						
							|  |  |  | 		/* Check for no next entry in the queue. */ | 
					
						
							|  |  |  | 		if (qp->r_head_ack_queue == qp->s_tail_ack_queue) { | 
					
						
							|  |  |  | 			if (qp->s_flags & IPATH_S_ACK_PENDING) | 
					
						
							|  |  |  | 				goto normal; | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 			qp->s_ack_state = OP(ACKNOWLEDGE); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			goto bail; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		e = &qp->s_ack_queue[qp->s_tail_ack_queue]; | 
					
						
							|  |  |  | 		if (e->opcode == OP(RDMA_READ_REQUEST)) { | 
					
						
							|  |  |  | 			/* Copy SGE state in case we need to resend */ | 
					
						
							|  |  |  | 			qp->s_ack_rdma_sge = e->rdma_sge; | 
					
						
							|  |  |  | 			qp->s_cur_sge = &qp->s_ack_rdma_sge; | 
					
						
							|  |  |  | 			len = e->rdma_sge.sge.sge_length; | 
					
						
							|  |  |  | 			if (len > pmtu) { | 
					
						
							|  |  |  | 				len = pmtu; | 
					
						
							|  |  |  | 				qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 				e->sent = 1; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			ohdr->u.aeth = ipath_compute_aeth(qp); | 
					
						
							|  |  |  | 			hwords++; | 
					
						
							|  |  |  | 			qp->s_ack_rdma_psn = e->psn; | 
					
						
							|  |  |  | 			bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* COMPARE_SWAP or FETCH_ADD */ | 
					
						
							|  |  |  | 			qp->s_cur_sge = NULL; | 
					
						
							|  |  |  | 			len = 0; | 
					
						
							|  |  |  | 			qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); | 
					
						
							|  |  |  | 			ohdr->u.at.aeth = ipath_compute_aeth(qp); | 
					
						
							|  |  |  | 			ohdr->u.at.atomic_ack_eth[0] = | 
					
						
							|  |  |  | 				cpu_to_be32(e->atomic_data >> 32); | 
					
						
							|  |  |  | 			ohdr->u.at.atomic_ack_eth[1] = | 
					
						
							|  |  |  | 				cpu_to_be32(e->atomic_data); | 
					
						
							|  |  |  | 			hwords += sizeof(ohdr->u.at) / sizeof(u32); | 
					
						
							|  |  |  | 			bth2 = e->psn; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 			e->sent = 1; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		bth0 = qp->s_ack_state << 24; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_FIRST): | 
					
						
							|  |  |  | 		qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE); | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_MIDDLE): | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		len = qp->s_ack_rdma_sge.sge.sge_length; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (len > pmtu) | 
					
						
							|  |  |  | 			len = pmtu; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			ohdr->u.aeth = ipath_compute_aeth(qp); | 
					
						
							|  |  |  | 			hwords++; | 
					
						
							|  |  |  | 			qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 			qp->s_ack_queue[qp->s_tail_ack_queue].sent = 1; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		bth0 = qp->s_ack_state << 24; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	normal: | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		 * Send a regular ACK. | 
					
						
							|  |  |  | 		 * Set the s_ack_state so we wait until after sending | 
					
						
							|  |  |  | 		 * the ACK before setting s_ack_state to ACKNOWLEDGE | 
					
						
							|  |  |  | 		 * (see above). | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 		qp->s_ack_state = OP(SEND_ONLY); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->s_flags &= ~IPATH_S_ACK_PENDING; | 
					
						
							|  |  |  | 		qp->s_cur_sge = NULL; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		if (qp->s_nak_state) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			ohdr->u.aeth = | 
					
						
							|  |  |  | 				cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | | 
					
						
							|  |  |  | 					    (qp->s_nak_state << | 
					
						
							|  |  |  | 					     IPATH_AETH_CREDIT_SHIFT)); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		else | 
					
						
							|  |  |  | 			ohdr->u.aeth = ipath_compute_aeth(qp); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		hwords++; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		len = 0; | 
					
						
							|  |  |  | 		bth0 = OP(ACKNOWLEDGE) << 24; | 
					
						
							|  |  |  | 		bth2 = qp->s_ack_psn & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	qp->s_hdrwords = hwords; | 
					
						
							|  |  |  | 	qp->s_cur_size = len; | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	ipath_make_ruc_header(dev, qp, ohdr, bth0, bth2); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	return 1; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | bail: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_make_rc_req - construct a request packet (SEND, RDMA r/w, ATOMIC) | 
					
						
							|  |  |  |  * @qp: a pointer to the QP | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Return 1 if constructed; otherwise, return 0. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | int ipath_make_rc_req(struct ipath_qp *qp) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ipath_ibdev *dev = to_idev(qp->ibqp.device); | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	struct ipath_other_headers *ohdr; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	struct ipath_sge_state *ss; | 
					
						
							|  |  |  | 	struct ipath_swqe *wqe; | 
					
						
							|  |  |  | 	u32 hwords; | 
					
						
							|  |  |  | 	u32 len; | 
					
						
							|  |  |  | 	u32 bth0; | 
					
						
							|  |  |  | 	u32 bth2; | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	char newreq; | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ohdr = &qp->s_hdr.u.oth; | 
					
						
							|  |  |  | 	if (qp->remote_ah_attr.ah_flags & IB_AH_GRH) | 
					
						
							|  |  |  | 		ohdr = &qp->s_hdr.u.l.oth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The lock is needed to synchronize between the sending tasklet, | 
					
						
							|  |  |  | 	 * the receive interrupt handler, and timeout resends. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	/* Sending responses has higher priority over sending requests. */ | 
					
						
							|  |  |  | 	if ((qp->r_head_ack_queue != qp->s_tail_ack_queue || | 
					
						
							|  |  |  | 	     (qp->s_flags & IPATH_S_ACK_PENDING) || | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 	     qp->s_ack_state != OP(ACKNOWLEDGE)) && | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	    ipath_make_rc_ack(dev, qp, ohdr, pmtu)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK)) { | 
					
						
							|  |  |  | 		if (!(ib_ipath_state_ops[qp->state] & IPATH_FLUSH_SEND)) | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		/* We are in the error state, flush the work request. */ | 
					
						
							|  |  |  | 		if (qp->s_last == qp->s_head) | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		/* If DMAs are in progress, we can't flush immediately. */ | 
					
						
							|  |  |  | 		if (atomic_read(&qp->s_dma_busy)) { | 
					
						
							|  |  |  | 			qp->s_flags |= IPATH_S_WAIT_DMA; | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							|  |  |  | 		ipath_send_complete(qp, wqe, IB_WC_WR_FLUSH_ERR); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	/* Leave BUSY set until RNR timeout. */ | 
					
						
							|  |  |  | 	if (qp->s_rnr_timeout) { | 
					
						
							|  |  |  | 		qp->s_flags |= IPATH_S_WAITING; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		goto bail; | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* header size in 32-bit words LRH+BTH = (8+12)/4. */ | 
					
						
							|  |  |  | 	hwords = 5; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:38 -07:00
										 |  |  | 	bth0 = 1 << 22; /* Set M bit */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Send a request. */ | 
					
						
							|  |  |  | 	wqe = get_swqe_ptr(qp, qp->s_cur); | 
					
						
							|  |  |  | 	switch (qp->s_state) { | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		if (!(ib_ipath_state_ops[qp->state] & | 
					
						
							|  |  |  | 		    IPATH_PROCESS_NEXT_SEND_OK)) | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Resend an old request or start a new one. | 
					
						
							|  |  |  | 		 * | 
					
						
							|  |  |  | 		 * We keep track of the current SWQE so that | 
					
						
							|  |  |  | 		 * we don't reset the "furthest progress" state | 
					
						
							|  |  |  | 		 * if we need to back up. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		newreq = 0; | 
					
						
							|  |  |  | 		if (qp->s_cur == qp->s_tail) { | 
					
						
							|  |  |  | 			/* Check if send work queue is empty. */ | 
					
						
							|  |  |  | 			if (qp->s_tail == qp->s_head) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				goto bail; | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * If a fence is requested, wait for previous | 
					
						
							|  |  |  | 			 * RDMA read and atomic operations to finish. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if ((wqe->wr.send_flags & IB_SEND_FENCE) && | 
					
						
							|  |  |  | 			    qp->s_num_rd_atomic) { | 
					
						
							|  |  |  | 				qp->s_flags |= IPATH_S_FENCE_PENDING; | 
					
						
							|  |  |  | 				goto bail; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 			wqe->psn = qp->s_next_psn; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			newreq = 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Note that we have to be careful not to modify the | 
					
						
							|  |  |  | 		 * original work request since we may need to resend | 
					
						
							|  |  |  | 		 * it. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 		len = wqe->length; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		ss = &qp->s_sge; | 
					
						
							|  |  |  | 		bth2 = 0; | 
					
						
							|  |  |  | 		switch (wqe->wr.opcode) { | 
					
						
							|  |  |  | 		case IB_WR_SEND: | 
					
						
							|  |  |  | 		case IB_WR_SEND_WITH_IMM: | 
					
						
							|  |  |  | 			/* If no credit, return. */ | 
					
						
							|  |  |  | 			if (qp->s_lsn != (u32) -1 && | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			    ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) { | 
					
						
							|  |  |  | 				qp->s_flags |= IPATH_S_WAIT_SSN_CREDIT; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				goto bail; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			wqe->lpsn = wqe->psn; | 
					
						
							|  |  |  | 			if (len > pmtu) { | 
					
						
							|  |  |  | 				wqe->lpsn += (len - 1) / pmtu; | 
					
						
							|  |  |  | 				qp->s_state = OP(SEND_FIRST); | 
					
						
							|  |  |  | 				len = pmtu; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (wqe->wr.opcode == IB_WR_SEND) | 
					
						
							|  |  |  | 				qp->s_state = OP(SEND_ONLY); | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				qp->s_state = OP(SEND_ONLY_WITH_IMMEDIATE); | 
					
						
							|  |  |  | 				/* Immediate data comes after the BTH */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:32 -07:00
										 |  |  | 				ohdr->u.imm_data = wqe->wr.ex.imm_data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 				hwords += 1; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (wqe->wr.send_flags & IB_SEND_SOLICITED) | 
					
						
							|  |  |  | 				bth0 |= 1 << 23; | 
					
						
							|  |  |  | 			bth2 = 1 << 31;	/* Request ACK. */ | 
					
						
							|  |  |  | 			if (++qp->s_cur == qp->s_size) | 
					
						
							|  |  |  | 				qp->s_cur = 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case IB_WR_RDMA_WRITE: | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 			if (newreq && qp->s_lsn != (u32) -1) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 				qp->s_lsn++; | 
					
						
							|  |  |  | 			/* FALLTHROUGH */ | 
					
						
							|  |  |  | 		case IB_WR_RDMA_WRITE_WITH_IMM: | 
					
						
							|  |  |  | 			/* If no credit, return. */ | 
					
						
							|  |  |  | 			if (qp->s_lsn != (u32) -1 && | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			    ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) { | 
					
						
							|  |  |  | 				qp->s_flags |= IPATH_S_WAIT_SSN_CREDIT; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				goto bail; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			ohdr->u.rc.reth.vaddr = | 
					
						
							|  |  |  | 				cpu_to_be64(wqe->wr.wr.rdma.remote_addr); | 
					
						
							|  |  |  | 			ohdr->u.rc.reth.rkey = | 
					
						
							|  |  |  | 				cpu_to_be32(wqe->wr.wr.rdma.rkey); | 
					
						
							|  |  |  | 			ohdr->u.rc.reth.length = cpu_to_be32(len); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			hwords += sizeof(struct ib_reth) / sizeof(u32); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			wqe->lpsn = wqe->psn; | 
					
						
							|  |  |  | 			if (len > pmtu) { | 
					
						
							|  |  |  | 				wqe->lpsn += (len - 1) / pmtu; | 
					
						
							|  |  |  | 				qp->s_state = OP(RDMA_WRITE_FIRST); | 
					
						
							|  |  |  | 				len = pmtu; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (wqe->wr.opcode == IB_WR_RDMA_WRITE) | 
					
						
							|  |  |  | 				qp->s_state = OP(RDMA_WRITE_ONLY); | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				qp->s_state = | 
					
						
							|  |  |  | 					OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 				/* Immediate data comes after RETH */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:32 -07:00
										 |  |  | 				ohdr->u.rc.imm_data = wqe->wr.ex.imm_data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 				hwords += 1; | 
					
						
							|  |  |  | 				if (wqe->wr.send_flags & IB_SEND_SOLICITED) | 
					
						
							|  |  |  | 					bth0 |= 1 << 23; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			bth2 = 1 << 31;	/* Request ACK. */ | 
					
						
							|  |  |  | 			if (++qp->s_cur == qp->s_size) | 
					
						
							|  |  |  | 				qp->s_cur = 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case IB_WR_RDMA_READ: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Don't allow more operations to be started | 
					
						
							|  |  |  | 			 * than the QP limits allow. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			if (newreq) { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				if (qp->s_num_rd_atomic >= | 
					
						
							|  |  |  | 				    qp->s_max_rd_atomic) { | 
					
						
							|  |  |  | 					qp->s_flags |= IPATH_S_RDMAR_PENDING; | 
					
						
							|  |  |  | 					goto bail; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				qp->s_num_rd_atomic++; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 				if (qp->s_lsn != (u32) -1) | 
					
						
							|  |  |  | 					qp->s_lsn++; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Adjust s_next_psn to count the | 
					
						
							|  |  |  | 				 * expected number of responses. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				if (len > pmtu) | 
					
						
							|  |  |  | 					qp->s_next_psn += (len - 1) / pmtu; | 
					
						
							|  |  |  | 				wqe->lpsn = qp->s_next_psn++; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			ohdr->u.rc.reth.vaddr = | 
					
						
							|  |  |  | 				cpu_to_be64(wqe->wr.wr.rdma.remote_addr); | 
					
						
							|  |  |  | 			ohdr->u.rc.reth.rkey = | 
					
						
							|  |  |  | 				cpu_to_be32(wqe->wr.wr.rdma.rkey); | 
					
						
							|  |  |  | 			ohdr->u.rc.reth.length = cpu_to_be32(len); | 
					
						
							|  |  |  | 			qp->s_state = OP(RDMA_READ_REQUEST); | 
					
						
							|  |  |  | 			hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			ss = NULL; | 
					
						
							|  |  |  | 			len = 0; | 
					
						
							|  |  |  | 			if (++qp->s_cur == qp->s_size) | 
					
						
							|  |  |  | 				qp->s_cur = 0; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case IB_WR_ATOMIC_CMP_AND_SWP: | 
					
						
							|  |  |  | 		case IB_WR_ATOMIC_FETCH_AND_ADD: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Don't allow more operations to be started | 
					
						
							|  |  |  | 			 * than the QP limits allow. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			if (newreq) { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 				if (qp->s_num_rd_atomic >= | 
					
						
							|  |  |  | 				    qp->s_max_rd_atomic) { | 
					
						
							|  |  |  | 					qp->s_flags |= IPATH_S_RDMAR_PENDING; | 
					
						
							|  |  |  | 					goto bail; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				qp->s_num_rd_atomic++; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 				if (qp->s_lsn != (u32) -1) | 
					
						
							|  |  |  | 					qp->s_lsn++; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 				wqe->lpsn = wqe->psn; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) { | 
					
						
							|  |  |  | 				qp->s_state = OP(COMPARE_SWAP); | 
					
						
							|  |  |  | 				ohdr->u.atomic_eth.swap_data = cpu_to_be64( | 
					
						
							|  |  |  | 					wqe->wr.wr.atomic.swap); | 
					
						
							|  |  |  | 				ohdr->u.atomic_eth.compare_data = cpu_to_be64( | 
					
						
							|  |  |  | 					wqe->wr.wr.atomic.compare_add); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				qp->s_state = OP(FETCH_ADD); | 
					
						
							|  |  |  | 				ohdr->u.atomic_eth.swap_data = cpu_to_be64( | 
					
						
							|  |  |  | 					wqe->wr.wr.atomic.compare_add); | 
					
						
							|  |  |  | 				ohdr->u.atomic_eth.compare_data = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32( | 
					
						
							|  |  |  | 				wqe->wr.wr.atomic.remote_addr >> 32); | 
					
						
							|  |  |  | 			ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32( | 
					
						
							|  |  |  | 				wqe->wr.wr.atomic.remote_addr); | 
					
						
							|  |  |  | 			ohdr->u.atomic_eth.rkey = cpu_to_be32( | 
					
						
							|  |  |  | 				wqe->wr.wr.atomic.rkey); | 
					
						
							|  |  |  | 			hwords += sizeof(struct ib_atomic_eth) / sizeof(u32); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			ss = NULL; | 
					
						
							|  |  |  | 			len = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			if (++qp->s_cur == qp->s_size) | 
					
						
							|  |  |  | 				qp->s_cur = 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			goto bail; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 		qp->s_sge.sge = wqe->sg_list[0]; | 
					
						
							|  |  |  | 		qp->s_sge.sg_list = wqe->sg_list + 1; | 
					
						
							|  |  |  | 		qp->s_sge.num_sge = wqe->wr.num_sge; | 
					
						
							|  |  |  | 		qp->s_len = wqe->length; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (newreq) { | 
					
						
							|  |  |  | 			qp->s_tail++; | 
					
						
							|  |  |  | 			if (qp->s_tail >= qp->s_size) | 
					
						
							|  |  |  | 				qp->s_tail = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 		bth2 |= qp->s_psn & IPATH_PSN_MASK; | 
					
						
							|  |  |  | 		if (wqe->wr.opcode == IB_WR_RDMA_READ) | 
					
						
							|  |  |  | 			qp->s_psn = wqe->lpsn + 1; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			qp->s_psn++; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 				qp->s_next_psn = qp->s_psn; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Put the QP on the pending list so lost ACKs will cause | 
					
						
							|  |  |  | 		 * a retry.  More than one request can be pending so the | 
					
						
							|  |  |  | 		 * QP may already be on the dev->pending list. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		spin_lock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2006-05-23 11:32:32 -07:00
										 |  |  | 		if (list_empty(&qp->timerwait)) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			list_add_tail(&qp->timerwait, | 
					
						
							|  |  |  | 				      &dev->pending[dev->pending_index]); | 
					
						
							|  |  |  | 		spin_unlock(&dev->pending_lock); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_FIRST): | 
					
						
							|  |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		 * This case can only happen if a send is restarted. | 
					
						
							|  |  |  | 		 * See ipath_restart_rc(). | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		 */ | 
					
						
							|  |  |  | 		ipath_init_restart(qp, wqe); | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(SEND_FIRST): | 
					
						
							|  |  |  | 		qp->s_state = OP(SEND_MIDDLE); | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(SEND_MIDDLE): | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		bth2 = qp->s_psn++ & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			qp->s_next_psn = qp->s_psn; | 
					
						
							|  |  |  | 		ss = &qp->s_sge; | 
					
						
							|  |  |  | 		len = qp->s_len; | 
					
						
							|  |  |  | 		if (len > pmtu) { | 
					
						
							|  |  |  | 			len = pmtu; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (wqe->wr.opcode == IB_WR_SEND) | 
					
						
							|  |  |  | 			qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE); | 
					
						
							|  |  |  | 			/* Immediate data comes after the BTH */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:32 -07:00
										 |  |  | 			ohdr->u.imm_data = wqe->wr.ex.imm_data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			hwords += 1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (wqe->wr.send_flags & IB_SEND_SOLICITED) | 
					
						
							|  |  |  | 			bth0 |= 1 << 23; | 
					
						
							|  |  |  | 		bth2 |= 1 << 31;	/* Request ACK. */ | 
					
						
							|  |  |  | 		qp->s_cur++; | 
					
						
							|  |  |  | 		if (qp->s_cur >= qp->s_size) | 
					
						
							|  |  |  | 			qp->s_cur = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_LAST): | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * This case can only happen if a RDMA write is restarted. | 
					
						
							|  |  |  | 		 * See ipath_restart_rc(). | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ipath_init_restart(qp, wqe); | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_FIRST): | 
					
						
							|  |  |  | 		qp->s_state = OP(RDMA_WRITE_MIDDLE); | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_MIDDLE): | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		bth2 = qp->s_psn++ & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			qp->s_next_psn = qp->s_psn; | 
					
						
							|  |  |  | 		ss = &qp->s_sge; | 
					
						
							|  |  |  | 		len = qp->s_len; | 
					
						
							|  |  |  | 		if (len > pmtu) { | 
					
						
							|  |  |  | 			len = pmtu; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (wqe->wr.opcode == IB_WR_RDMA_WRITE) | 
					
						
							|  |  |  | 			qp->s_state = OP(RDMA_WRITE_LAST); | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			qp->s_state = OP(RDMA_WRITE_LAST_WITH_IMMEDIATE); | 
					
						
							|  |  |  | 			/* Immediate data comes after the BTH */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:32 -07:00
										 |  |  | 			ohdr->u.imm_data = wqe->wr.ex.imm_data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			hwords += 1; | 
					
						
							|  |  |  | 			if (wqe->wr.send_flags & IB_SEND_SOLICITED) | 
					
						
							|  |  |  | 				bth0 |= 1 << 23; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		bth2 |= 1 << 31;	/* Request ACK. */ | 
					
						
							|  |  |  | 		qp->s_cur++; | 
					
						
							|  |  |  | 		if (qp->s_cur >= qp->s_size) | 
					
						
							|  |  |  | 			qp->s_cur = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_MIDDLE): | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * This case can only happen if a RDMA read is restarted. | 
					
						
							|  |  |  | 		 * See ipath_restart_rc(). | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ipath_init_restart(qp, wqe); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) * pmtu; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		ohdr->u.rc.reth.vaddr = | 
					
						
							|  |  |  | 			cpu_to_be64(wqe->wr.wr.rdma.remote_addr + len); | 
					
						
							|  |  |  | 		ohdr->u.rc.reth.rkey = | 
					
						
							|  |  |  | 			cpu_to_be32(wqe->wr.wr.rdma.rkey); | 
					
						
							|  |  |  | 		ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len); | 
					
						
							|  |  |  | 		qp->s_state = OP(RDMA_READ_REQUEST); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); | 
					
						
							| 
									
										
										
										
											2008-12-05 11:13:18 -08:00
										 |  |  | 		bth2 = qp->s_psn & IPATH_PSN_MASK; | 
					
						
							|  |  |  | 		qp->s_psn = wqe->lpsn + 1; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		ss = NULL; | 
					
						
							|  |  |  | 		len = 0; | 
					
						
							|  |  |  | 		qp->s_cur++; | 
					
						
							|  |  |  | 		if (qp->s_cur == qp->s_size) | 
					
						
							|  |  |  | 			qp->s_cur = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 	if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0) | 
					
						
							|  |  |  | 		bth2 |= 1 << 31;	/* Request ACK. */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	qp->s_len -= len; | 
					
						
							|  |  |  | 	qp->s_hdrwords = hwords; | 
					
						
							|  |  |  | 	qp->s_cur_sge = ss; | 
					
						
							|  |  |  | 	qp->s_cur_size = len; | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	ipath_make_ruc_header(dev, qp, ohdr, bth0 | (qp->s_state << 24), bth2); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | done: | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	ret = 1; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	goto unlock; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | bail: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	qp->s_flags &= ~IPATH_S_BUSY; | 
					
						
							|  |  |  | unlock: | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  |  * send_rc_ack - Construct an ACK packet and send it | 
					
						
							|  |  |  |  * @qp: a pointer to the QP | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  |  * This is called from ipath_rc_rcv() and only uses the receive | 
					
						
							|  |  |  |  * side QP state. | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  |  * Note that RDMA reads and atomics are handled in the | 
					
						
							|  |  |  |  * send side QP state and tasklet. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  */ | 
					
						
							|  |  |  | static void send_rc_ack(struct ipath_qp *qp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ipath_ibdev *dev = to_idev(qp->ibqp.device); | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	struct ipath_devdata *dd; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	u16 lrh0; | 
					
						
							|  |  |  | 	u32 bth0; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	u32 hwords; | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	u32 __iomem *piobuf; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	struct ipath_ib_header hdr; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	struct ipath_other_headers *ohdr; | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	/* Don't send ACK or NAK if a RDMA read or atomic is pending. */ | 
					
						
							| 
									
										
										
										
											2007-05-03 12:43:03 -07:00
										 |  |  | 	if (qp->r_head_ack_queue != qp->s_tail_ack_queue || | 
					
						
							|  |  |  | 	    (qp->s_flags & IPATH_S_ACK_PENDING) || | 
					
						
							|  |  |  | 	    qp->s_ack_state != OP(ACKNOWLEDGE)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		goto queue_ack; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	/* Don't try to send ACKs if the link isn't ACTIVE */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	dd = dev->dd; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	if (!(dd->ipath_flags & IPATH_LINKACTIVE)) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	piobuf = ipath_getpiobuf(dd, 0, NULL); | 
					
						
							|  |  |  | 	if (!piobuf) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * We are out of PIO buffers at the moment. | 
					
						
							|  |  |  | 		 * Pass responsibility for sending the ACK to the | 
					
						
							|  |  |  | 		 * send tasklet so that when a PIO buffer becomes | 
					
						
							|  |  |  | 		 * available, the ACK is sent ahead of other outgoing | 
					
						
							|  |  |  | 		 * packets. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							|  |  |  | 		goto queue_ack; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* Construct the header. */ | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	ohdr = &hdr.u.oth; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 	lrh0 = IPATH_LRH_BTH; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* header size in 32-bit words LRH+BTH+AETH = (8+12+4)/4. */ | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	hwords = 6; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) { | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 		hwords += ipath_make_grh(dev, &hdr.u.l.grh, | 
					
						
							|  |  |  | 					 &qp->remote_ah_attr.grh, | 
					
						
							|  |  |  | 					 hwords, 0); | 
					
						
							|  |  |  | 		ohdr = &hdr.u.l.oth; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		lrh0 = IPATH_LRH_GRH; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	/* read pkey_index w/o lock (its atomic) */ | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	bth0 = ipath_get_pkey(dd, qp->s_pkey_index) | | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:38 -07:00
										 |  |  | 		(OP(ACKNOWLEDGE) << 24) | (1 << 22); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	if (qp->r_nak_state) | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 					    (qp->r_nak_state << | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 					     IPATH_AETH_CREDIT_SHIFT)); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		ohdr->u.aeth = ipath_compute_aeth(qp); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	lrh0 |= qp->remote_ah_attr.sl << 4; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	hdr.lrh[0] = cpu_to_be16(lrh0); | 
					
						
							|  |  |  | 	hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); | 
					
						
							|  |  |  | 	hdr.lrh[2] = cpu_to_be16(hwords + SIZE_OF_CRC); | 
					
						
							| 
									
										
										
										
											2008-09-20 20:05:51 -07:00
										 |  |  | 	hdr.lrh[3] = cpu_to_be16(dd->ipath_lid | | 
					
						
							|  |  |  | 				 qp->remote_ah_attr.src_path_bits); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	ohdr->bth[0] = cpu_to_be32(bth0); | 
					
						
							|  |  |  | 	ohdr->bth[1] = cpu_to_be32(qp->remote_qpn); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 	ohdr->bth[2] = cpu_to_be32(qp->r_ack_psn & IPATH_PSN_MASK); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	writeq(hwords + 1, piobuf); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:27 -07:00
										 |  |  | 	if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) { | 
					
						
							|  |  |  | 		u32 *hdrp = (u32 *) &hdr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ipath_flush_wc(); | 
					
						
							|  |  |  | 		__iowrite32_copy(piobuf + 2, hdrp, hwords - 1); | 
					
						
							|  |  |  | 		ipath_flush_wc(); | 
					
						
							|  |  |  | 		__raw_writel(hdrp[hwords - 1], piobuf + hwords + 1); | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		__iowrite32_copy(piobuf + 2, (u32 *) &hdr, hwords); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ipath_flush_wc(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->n_unicast_xmit++; | 
					
						
							|  |  |  | 	goto done; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | queue_ack: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	if (ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK) { | 
					
						
							|  |  |  | 		dev->n_rc_qacks++; | 
					
						
							|  |  |  | 		qp->s_flags |= IPATH_S_ACK_PENDING; | 
					
						
							|  |  |  | 		qp->s_nak_state = qp->r_nak_state; | 
					
						
							|  |  |  | 		qp->s_ack_psn = qp->r_ack_psn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Schedule the send tasklet. */ | 
					
						
							|  |  |  | 		ipath_schedule_send(qp); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | done: | 
					
						
							|  |  |  | 	return; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * reset_psn - reset the QP state to send starting from PSN | 
					
						
							|  |  |  |  * @qp: the QP | 
					
						
							|  |  |  |  * @psn: the packet sequence number to restart at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called from ipath_rc_rcv() to process an incoming RC ACK | 
					
						
							|  |  |  |  * for the given QP. | 
					
						
							|  |  |  |  * Called at interrupt level with the QP s_lock held. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void reset_psn(struct ipath_qp *qp, u32 psn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 n = qp->s_last; | 
					
						
							|  |  |  | 	struct ipath_swqe *wqe = get_swqe_ptr(qp, n); | 
					
						
							|  |  |  | 	u32 opcode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	qp->s_cur = n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If we are starting the request from the beginning, | 
					
						
							|  |  |  | 	 * let the normal send code handle initialization. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (ipath_cmp24(psn, wqe->psn) <= 0) { | 
					
						
							|  |  |  | 		qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Find the work request opcode corresponding to the given PSN. */ | 
					
						
							|  |  |  | 	opcode = wqe->wr.opcode; | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		int diff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (++n == qp->s_size) | 
					
						
							|  |  |  | 			n = 0; | 
					
						
							|  |  |  | 		if (n == qp->s_tail) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		wqe = get_swqe_ptr(qp, n); | 
					
						
							|  |  |  | 		diff = ipath_cmp24(psn, wqe->psn); | 
					
						
							|  |  |  | 		if (diff < 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		qp->s_cur = n; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * If we are starting the request from the beginning, | 
					
						
							|  |  |  | 		 * let the normal send code handle initialization. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (diff == 0) { | 
					
						
							|  |  |  | 			qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 			goto done; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		opcode = wqe->wr.opcode; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set the state to restart in the middle of a request. | 
					
						
							|  |  |  | 	 * Don't change the s_sge, s_cur_sge, or s_cur_size. | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	 * See ipath_make_rc_req(). | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	switch (opcode) { | 
					
						
							|  |  |  | 	case IB_WR_SEND: | 
					
						
							|  |  |  | 	case IB_WR_SEND_WITH_IMM: | 
					
						
							|  |  |  | 		qp->s_state = OP(RDMA_READ_RESPONSE_FIRST); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case IB_WR_RDMA_WRITE: | 
					
						
							|  |  |  | 	case IB_WR_RDMA_WRITE_WITH_IMM: | 
					
						
							|  |  |  | 		qp->s_state = OP(RDMA_READ_RESPONSE_LAST); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case IB_WR_RDMA_READ: | 
					
						
							|  |  |  | 		qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * This case shouldn't happen since its only | 
					
						
							|  |  |  | 		 * one PSN per req. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  | 	qp->s_psn = psn; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_restart_rc - back up requester to resend the last un-ACKed request | 
					
						
							|  |  |  |  * @qp: the QP to restart | 
					
						
							|  |  |  |  * @psn: packet sequence number for the request | 
					
						
							|  |  |  |  * @wc: the work completion request | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  |  * The QP s_lock should be held and interrupts disabled. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | void ipath_restart_rc(struct ipath_qp *qp, u32 psn) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							|  |  |  | 	struct ipath_ibdev *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (qp->s_retry == 0) { | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 		ipath_send_complete(qp, wqe, IB_WC_RETRY_EXC_ERR); | 
					
						
							|  |  |  | 		ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		goto bail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	qp->s_retry--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Remove the QP from the timeout queue. | 
					
						
							|  |  |  | 	 * Note: it may already have been removed by ipath_ib_timer(). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	dev = to_idev(qp->ibqp.device); | 
					
						
							|  |  |  | 	spin_lock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2006-05-23 11:32:32 -07:00
										 |  |  | 	if (!list_empty(&qp->timerwait)) | 
					
						
							|  |  |  | 		list_del_init(&qp->timerwait); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	if (!list_empty(&qp->piowait)) | 
					
						
							|  |  |  | 		list_del_init(&qp->piowait); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	spin_unlock(&dev->pending_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (wqe->wr.opcode == IB_WR_RDMA_READ) | 
					
						
							|  |  |  | 		dev->n_rc_resends++; | 
					
						
							|  |  |  | 	else | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 	reset_psn(qp, psn); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | bail: | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | static inline void update_last_psn(struct ipath_qp *qp, u32 psn) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	qp->s_last_psn = psn; | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * do_rc_ack - process an incoming RC ACK | 
					
						
							|  |  |  |  * @qp: the QP the ACK came in on | 
					
						
							|  |  |  |  * @psn: the packet sequence number of the ACK | 
					
						
							|  |  |  |  * @opcode: the opcode of the request that resulted in the ACK | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  |  * This is called from ipath_rc_rcv_resp() to process an incoming RC ACK | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  * for the given QP. | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  |  * Called at interrupt level with the QP s_lock held and interrupts disabled. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  * Returns 1 if OK, 0 if current operation should be aborted (NAK). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode, | 
					
						
							|  |  |  | 		     u64 val) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ipath_ibdev *dev = to_idev(qp->ibqp.device); | 
					
						
							|  |  |  | 	struct ib_wc wc; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	enum ib_wc_status status; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	struct ipath_swqe *wqe; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 	u32 ack_psn; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 	int diff; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Remove the QP from the timeout queue (or RNR timeout queue). | 
					
						
							|  |  |  | 	 * If ipath_ib_timer() has already removed it, | 
					
						
							|  |  |  | 	 * it's OK since we hold the QP s_lock and ipath_restart_rc() | 
					
						
							|  |  |  | 	 * just won't find anything to restart if we ACK everything. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	spin_lock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2006-05-23 11:32:32 -07:00
										 |  |  | 	if (!list_empty(&qp->timerwait)) | 
					
						
							|  |  |  | 		list_del_init(&qp->timerwait); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	spin_unlock(&dev->pending_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Note that NAKs implicitly ACK outstanding SEND and RDMA write | 
					
						
							|  |  |  | 	 * requests and implicitly NAK RDMA read and atomic requests issued | 
					
						
							|  |  |  | 	 * before the NAK'ed request.  The MSN won't include the NAK'ed | 
					
						
							|  |  |  | 	 * request but will include an ACK'ed request(s). | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-09-29 14:37:51 -07:00
										 |  |  | 	ack_psn = psn; | 
					
						
							|  |  |  | 	if (aeth >> 29) | 
					
						
							|  |  |  | 		ack_psn--; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The MSN might be for a later WQE than the PSN indicates so | 
					
						
							|  |  |  | 	 * only complete WQEs that the PSN finishes. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 	while ((diff = ipath_cmp24(ack_psn, wqe->lpsn)) >= 0) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * RDMA_READ_RESPONSE_ONLY is a special case since | 
					
						
							|  |  |  | 		 * we want to generate completion events for everything | 
					
						
							|  |  |  | 		 * before the RDMA read, copy the data, then generate | 
					
						
							|  |  |  | 		 * the completion for the read. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (wqe->wr.opcode == IB_WR_RDMA_READ && | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_READ_RESPONSE_ONLY) && | 
					
						
							|  |  |  | 		    diff == 0) { | 
					
						
							|  |  |  | 			ret = 1; | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * If this request is a RDMA read or atomic, and the ACK is | 
					
						
							|  |  |  | 		 * for a later operation, this ACK NAKs the RDMA read or | 
					
						
							|  |  |  | 		 * atomic.  In other words, only a RDMA_READ_LAST or ONLY | 
					
						
							|  |  |  | 		 * can ACK a RDMA read and likewise for atomic ops.  Note | 
					
						
							|  |  |  | 		 * that the NAK case can only happen if relaxed ordering is | 
					
						
							|  |  |  | 		 * used and requests are sent after an RDMA read or atomic | 
					
						
							|  |  |  | 		 * is sent but before the response is received. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if ((wqe->wr.opcode == IB_WR_RDMA_READ && | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		     (opcode != OP(RDMA_READ_RESPONSE_LAST) || diff != 0)) || | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		    ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || | 
					
						
							|  |  |  | 		      wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		     (opcode != OP(ATOMIC_ACKNOWLEDGE) || diff != 0))) { | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * The last valid PSN seen is the previous | 
					
						
							|  |  |  | 			 * request's. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 			update_last_psn(qp, wqe->psn - 1); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			/* Retry this request. */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			ipath_restart_rc(qp, wqe->psn); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * No need to process the ACK/NAK since we are | 
					
						
							|  |  |  | 			 * restarting an earlier request. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || | 
					
						
							|  |  |  | 		    wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) | 
					
						
							|  |  |  | 			*(u64 *) wqe->sg_list[0].vaddr = val; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (qp->s_num_rd_atomic && | 
					
						
							|  |  |  | 		    (wqe->wr.opcode == IB_WR_RDMA_READ || | 
					
						
							|  |  |  | 		     wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || | 
					
						
							|  |  |  | 		     wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)) { | 
					
						
							|  |  |  | 			qp->s_num_rd_atomic--; | 
					
						
							|  |  |  | 			/* Restart sending task if fence is complete */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			if (((qp->s_flags & IPATH_S_FENCE_PENDING) && | 
					
						
							|  |  |  | 			     !qp->s_num_rd_atomic) || | 
					
						
							|  |  |  | 			    qp->s_flags & IPATH_S_RDMAR_PENDING) | 
					
						
							|  |  |  | 				ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/* Post a send completion queue entry if requested. */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		    (wqe->wr.send_flags & IB_SEND_SIGNALED)) { | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			memset(&wc, 0, sizeof wc); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			wc.wr_id = wqe->wr.wr_id; | 
					
						
							|  |  |  | 			wc.status = IB_WC_SUCCESS; | 
					
						
							|  |  |  | 			wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; | 
					
						
							|  |  |  | 			wc.byte_len = wqe->length; | 
					
						
							| 
									
										
										
										
											2006-12-31 21:09:42 +02:00
										 |  |  | 			wc.qp = &qp->ibqp; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			wc.src_qp = qp->remote_qpn; | 
					
						
							|  |  |  | 			wc.slid = qp->remote_ah_attr.dlid; | 
					
						
							|  |  |  | 			wc.sl = qp->remote_ah_attr.sl; | 
					
						
							|  |  |  | 			ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		qp->s_retry = qp->s_retry_cnt; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * If we are completing a request which is in the process of | 
					
						
							|  |  |  | 		 * being resent, we can stop resending it since we know the | 
					
						
							|  |  |  | 		 * responder has already seen it. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (qp->s_last == qp->s_cur) { | 
					
						
							|  |  |  | 			if (++qp->s_cur >= qp->s_size) | 
					
						
							|  |  |  | 				qp->s_cur = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			qp->s_last = qp->s_cur; | 
					
						
							|  |  |  | 			if (qp->s_last == qp->s_tail) | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			wqe = get_swqe_ptr(qp, qp->s_cur); | 
					
						
							|  |  |  | 			qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 			qp->s_psn = wqe->psn; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			if (++qp->s_last >= qp->s_size) | 
					
						
							|  |  |  | 				qp->s_last = 0; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			if (qp->state == IB_QPS_SQD && qp->s_last == qp->s_cur) | 
					
						
							|  |  |  | 				qp->s_draining = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			if (qp->s_last == qp->s_tail) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (aeth >> 29) { | 
					
						
							|  |  |  | 	case 0:		/* ACK */ | 
					
						
							|  |  |  | 		dev->n_rc_acks++; | 
					
						
							|  |  |  | 		/* If this is a partial ACK, reset the retransmit timer. */ | 
					
						
							|  |  |  | 		if (qp->s_last != qp->s_tail) { | 
					
						
							|  |  |  | 			spin_lock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2007-10-26 08:02:39 -07:00
										 |  |  | 			if (list_empty(&qp->timerwait)) | 
					
						
							|  |  |  | 				list_add_tail(&qp->timerwait, | 
					
						
							|  |  |  | 					&dev->pending[dev->pending_index]); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			spin_unlock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * If we get a partial ACK for a resent operation, | 
					
						
							|  |  |  | 			 * we can stop resending the earlier packets and | 
					
						
							|  |  |  | 			 * continue with the next packet the receiver wants. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (ipath_cmp24(qp->s_psn, psn) <= 0) { | 
					
						
							|  |  |  | 				reset_psn(qp, psn + 1); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 				ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else if (ipath_cmp24(qp->s_psn, psn) <= 0) { | 
					
						
							|  |  |  | 			qp->s_state = OP(SEND_LAST); | 
					
						
							|  |  |  | 			qp->s_psn = psn + 1; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		ipath_get_credit(qp, aeth); | 
					
						
							|  |  |  | 		qp->s_rnr_retry = qp->s_rnr_retry_cnt; | 
					
						
							|  |  |  | 		qp->s_retry = qp->s_retry_cnt; | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 		update_last_psn(qp, psn); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		ret = 1; | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 1:		/* RNR NAK */ | 
					
						
							|  |  |  | 		dev->n_rnr_naks++; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		if (qp->s_last == qp->s_tail) | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (qp->s_rnr_retry == 0) { | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			status = IB_WC_RNR_RETRY_EXC_ERR; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			goto class_b; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (qp->s_rnr_retry_cnt < 7) | 
					
						
							|  |  |  | 			qp->s_rnr_retry--; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		/* The last valid PSN is the previous PSN. */ | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 		update_last_psn(qp, psn - 1); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		if (wqe->wr.opcode == IB_WR_RDMA_READ) | 
					
						
							|  |  |  | 			dev->n_rc_resends++; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			dev->n_rc_resends += | 
					
						
							|  |  |  | 				(qp->s_psn - psn) & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		reset_psn(qp, psn); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		qp->s_rnr_timeout = | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 			ib_ipath_rnr_table[(aeth >> IPATH_AETH_CREDIT_SHIFT) & | 
					
						
							|  |  |  | 					   IPATH_AETH_CREDIT_MASK]; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		ipath_insert_rnr_queue(qp); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		goto bail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 3:		/* NAK */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 		if (qp->s_last == qp->s_tail) | 
					
						
							|  |  |  | 			goto bail; | 
					
						
							|  |  |  | 		/* The last valid PSN is the previous PSN. */ | 
					
						
							|  |  |  | 		update_last_psn(qp, psn - 1); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:17 -07:00
										 |  |  | 		switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) & | 
					
						
							|  |  |  | 			IPATH_AETH_CREDIT_MASK) { | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		case 0:	/* PSN sequence error */ | 
					
						
							|  |  |  | 			dev->n_seq_naks++; | 
					
						
							|  |  |  | 			/*
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:54 -07:00
										 |  |  | 			 * Back up to the responder's expected PSN. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			 * Note that we might get a NAK in the middle of an | 
					
						
							|  |  |  | 			 * RDMA READ response which terminates the RDMA | 
					
						
							|  |  |  | 			 * READ. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			ipath_restart_rc(qp, psn); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case 1:	/* Invalid Request */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			status = IB_WC_REM_INV_REQ_ERR; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			dev->n_other_naks++; | 
					
						
							|  |  |  | 			goto class_b; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case 2:	/* Remote Access Error */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			status = IB_WC_REM_ACCESS_ERR; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			dev->n_other_naks++; | 
					
						
							|  |  |  | 			goto class_b; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case 3:	/* Remote Operation Error */ | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			status = IB_WC_REM_OP_ERR; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			dev->n_other_naks++; | 
					
						
							|  |  |  | 		class_b: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			ipath_send_complete(qp, wqe, status); | 
					
						
							|  |  |  | 			ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			/* Ignore other reserved NAK error codes */ | 
					
						
							|  |  |  | 			goto reserved; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		qp->s_rnr_retry = qp->s_rnr_retry_cnt; | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default:		/* 2: reserved */ | 
					
						
							|  |  |  | 	reserved: | 
					
						
							|  |  |  | 		/* Ignore reserved NAK codes. */ | 
					
						
							|  |  |  | 		goto bail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bail: | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_rc_rcv_resp - process an incoming RC response packet | 
					
						
							|  |  |  |  * @dev: the device this packet came in on | 
					
						
							|  |  |  |  * @ohdr: the other headers for this packet | 
					
						
							|  |  |  |  * @data: the packet data | 
					
						
							|  |  |  |  * @tlen: the packet length | 
					
						
							|  |  |  |  * @qp: the QP for this packet | 
					
						
							|  |  |  |  * @opcode: the opcode for this packet | 
					
						
							|  |  |  |  * @psn: the packet sequence number for this packet | 
					
						
							|  |  |  |  * @hdrsize: the header length | 
					
						
							|  |  |  |  * @pmtu: the path MTU | 
					
						
							|  |  |  |  * @header_in_data: true if part of the header data is in the data buffer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called from ipath_rc_rcv() to process an incoming RC response | 
					
						
							|  |  |  |  * packet for the given QP. | 
					
						
							|  |  |  |  * Called at interrupt level. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, | 
					
						
							|  |  |  | 				     struct ipath_other_headers *ohdr, | 
					
						
							|  |  |  | 				     void *data, u32 tlen, | 
					
						
							|  |  |  | 				     struct ipath_qp *qp, | 
					
						
							|  |  |  | 				     u32 opcode, | 
					
						
							|  |  |  | 				     u32 psn, u32 hdrsize, u32 pmtu, | 
					
						
							|  |  |  | 				     int header_in_data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	struct ipath_swqe *wqe; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	enum ib_wc_status status; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int diff; | 
					
						
							|  |  |  | 	u32 pad; | 
					
						
							|  |  |  | 	u32 aeth; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 	u64 val; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	/* Double check we can process this now that we hold the s_lock. */ | 
					
						
							|  |  |  | 	if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) | 
					
						
							|  |  |  | 		goto ack_done; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* Ignore invalid responses. */ | 
					
						
							|  |  |  | 	if (ipath_cmp24(psn, qp->s_next_psn) >= 0) | 
					
						
							|  |  |  | 		goto ack_done; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Ignore duplicate responses. */ | 
					
						
							|  |  |  | 	diff = ipath_cmp24(psn, qp->s_last_psn); | 
					
						
							|  |  |  | 	if (unlikely(diff <= 0)) { | 
					
						
							|  |  |  | 		/* Update credits for "ghost" ACKs */ | 
					
						
							|  |  |  | 		if (diff == 0 && opcode == OP(ACKNOWLEDGE)) { | 
					
						
							|  |  |  | 			if (!header_in_data) | 
					
						
							|  |  |  | 				aeth = be32_to_cpu(ohdr->u.aeth); | 
					
						
							|  |  |  | 			else { | 
					
						
							|  |  |  | 				aeth = be32_to_cpu(((__be32 *) data)[0]); | 
					
						
							|  |  |  | 				data += sizeof(__be32); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if ((aeth >> 29) == 0) | 
					
						
							|  |  |  | 				ipath_get_credit(qp, aeth); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		goto ack_done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	if (unlikely(qp->s_last == qp->s_tail)) | 
					
						
							|  |  |  | 		goto ack_done; | 
					
						
							|  |  |  | 	wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	status = IB_WC_SUCCESS; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	switch (opcode) { | 
					
						
							|  |  |  | 	case OP(ACKNOWLEDGE): | 
					
						
							|  |  |  | 	case OP(ATOMIC_ACKNOWLEDGE): | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_FIRST): | 
					
						
							|  |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(ohdr->u.aeth); | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(((__be32 *) data)[0]); | 
					
						
							|  |  |  | 			data += sizeof(__be32); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (opcode == OP(ATOMIC_ACKNOWLEDGE)) { | 
					
						
							|  |  |  | 			if (!header_in_data) { | 
					
						
							|  |  |  | 				__be32 *p = ohdr->u.at.atomic_ack_eth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				val = ((u64) be32_to_cpu(p[0]) << 32) | | 
					
						
							|  |  |  | 					be32_to_cpu(p[1]); | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 				val = be64_to_cpu(((__be64 *) data)[0]); | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		} else | 
					
						
							|  |  |  | 			val = 0; | 
					
						
							|  |  |  | 		if (!do_rc_ack(qp, aeth, psn, opcode, val) || | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		    opcode != OP(RDMA_READ_RESPONSE_FIRST)) | 
					
						
							|  |  |  | 			goto ack_done; | 
					
						
							|  |  |  | 		hdrsize += 4; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 			goto ack_op_err; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:42:20 -07:00
										 |  |  | 		qp->r_flags &= ~IPATH_R_RDMAR_SEQ; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		 * If this is a response to a resent RDMA read, we | 
					
						
							|  |  |  | 		 * have to be careful to copy the data to the right | 
					
						
							|  |  |  | 		 * location. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, | 
					
						
							|  |  |  | 						  wqe, psn, pmtu); | 
					
						
							|  |  |  | 		goto read_middle; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_READ_RESPONSE_MIDDLE): | 
					
						
							|  |  |  | 		/* no AETH, no ACK */ | 
					
						
							|  |  |  | 		if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { | 
					
						
							|  |  |  | 			dev->n_rdma_seq++; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:42:20 -07:00
										 |  |  | 			if (qp->r_flags & IPATH_R_RDMAR_SEQ) | 
					
						
							|  |  |  | 				goto ack_done; | 
					
						
							|  |  |  | 			qp->r_flags |= IPATH_R_RDMAR_SEQ; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			ipath_restart_rc(qp, qp->s_last_psn + 1); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			goto ack_done; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 			goto ack_op_err; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	read_middle: | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		if (unlikely(tlen != (hdrsize + pmtu + 4))) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 			goto ack_len_err; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (unlikely(pmtu >= qp->s_rdma_read_len)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 			goto ack_len_err; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		/* We got a response so update the timeout. */ | 
					
						
							|  |  |  | 		spin_lock(&dev->pending_lock); | 
					
						
							|  |  |  | 		if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait)) | 
					
						
							|  |  |  | 			list_move_tail(&qp->timerwait, | 
					
						
							|  |  |  | 				       &dev->pending[dev->pending_index]); | 
					
						
							|  |  |  | 		spin_unlock(&dev->pending_lock); | 
					
						
							| 
									
										
										
										
											2008-02-20 19:08:10 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (opcode == OP(RDMA_READ_RESPONSE_MIDDLE)) | 
					
						
							|  |  |  | 			qp->s_retry = qp->s_retry_cnt; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		 * Update the RDMA receive state but do the copy w/o | 
					
						
							|  |  |  | 		 * holding the locks and blocking interrupts. | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->s_rdma_read_len -= pmtu; | 
					
						
							| 
									
										
										
										
											2006-09-28 08:59:57 -07:00
										 |  |  | 		update_last_psn(qp, psn); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:51 -07:00
										 |  |  | 		goto bail; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	case OP(RDMA_READ_RESPONSE_ONLY): | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(ohdr->u.aeth); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(((__be32 *) data)[0]); | 
					
						
							|  |  |  | 		if (!do_rc_ack(qp, aeth, psn, opcode, 0)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			goto ack_done; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 		/* Get the number of bytes the message was padded by. */ | 
					
						
							|  |  |  | 		pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Check that the data size is >= 0 && <= pmtu. | 
					
						
							|  |  |  | 		 * Remember to account for the AETH header (4) and | 
					
						
							|  |  |  | 		 * ICRC (4). | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (unlikely(tlen < (hdrsize + pad + 8))) | 
					
						
							|  |  |  | 			goto ack_len_err; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * If this is a response to a resent RDMA read, we | 
					
						
							|  |  |  | 		 * have to be careful to copy the data to the right | 
					
						
							|  |  |  | 		 * location. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		wqe = get_swqe_ptr(qp, qp->s_last); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, | 
					
						
							|  |  |  | 						  wqe, psn, pmtu); | 
					
						
							|  |  |  | 		goto read_last; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	case OP(RDMA_READ_RESPONSE_LAST): | 
					
						
							|  |  |  | 		/* ACKs READ req. */ | 
					
						
							|  |  |  | 		if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { | 
					
						
							|  |  |  | 			dev->n_rdma_seq++; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:42:20 -07:00
										 |  |  | 			if (qp->r_flags & IPATH_R_RDMAR_SEQ) | 
					
						
							|  |  |  | 				goto ack_done; | 
					
						
							|  |  |  | 			qp->r_flags |= IPATH_R_RDMAR_SEQ; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 			ipath_restart_rc(qp, qp->s_last_psn + 1); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			goto ack_done; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 			goto ack_op_err; | 
					
						
							|  |  |  | 		/* Get the number of bytes the message was padded by. */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Check that the data size is >= 1 && <= pmtu. | 
					
						
							|  |  |  | 		 * Remember to account for the AETH header (4) and | 
					
						
							|  |  |  | 		 * ICRC (4). | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 		if (unlikely(tlen <= (hdrsize + pad + 8))) | 
					
						
							|  |  |  | 			goto ack_len_err; | 
					
						
							|  |  |  | 	read_last: | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		tlen -= hdrsize + pad + 8; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 		if (unlikely(tlen != qp->s_rdma_read_len)) | 
					
						
							|  |  |  | 			goto ack_len_err; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(ohdr->u.aeth); | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			aeth = be32_to_cpu(((__be32 *) data)[0]); | 
					
						
							|  |  |  | 			data += sizeof(__be32); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen); | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		(void) do_rc_ack(qp, aeth, psn, | 
					
						
							|  |  |  | 				 OP(RDMA_READ_RESPONSE_LAST), 0); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		goto ack_done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | ack_op_err: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	status = IB_WC_LOC_QP_OP_ERR; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | 	goto ack_err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ack_len_err: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	status = IB_WC_LOC_LEN_ERR; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:45:00 -07:00
										 |  |  | ack_err: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	ipath_send_complete(qp, wqe, status); | 
					
						
							|  |  |  | 	ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR); | 
					
						
							|  |  |  | ack_done: | 
					
						
							| 
									
										
										
										
											2007-05-03 12:40:51 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | bail: | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_rc_rcv_error - process an incoming duplicate or error RC packet | 
					
						
							|  |  |  |  * @dev: the device this packet came in on | 
					
						
							|  |  |  |  * @ohdr: the other headers for this packet | 
					
						
							|  |  |  |  * @data: the packet data | 
					
						
							|  |  |  |  * @qp: the QP for this packet | 
					
						
							|  |  |  |  * @opcode: the opcode for this packet | 
					
						
							|  |  |  |  * @psn: the packet sequence number for this packet | 
					
						
							|  |  |  |  * @diff: the difference between the PSN and the expected PSN | 
					
						
							|  |  |  |  * @header_in_data: true if part of the header data is in the data buffer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called from ipath_rc_rcv() to process an unexpected | 
					
						
							|  |  |  |  * incoming RC packet for the given QP. | 
					
						
							|  |  |  |  * Called at interrupt level. | 
					
						
							|  |  |  |  * Return 1 if no more processing is needed; otherwise return 0 to | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  |  * schedule a response to be sent. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  |  */ | 
					
						
							|  |  |  | static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, | 
					
						
							|  |  |  | 				     struct ipath_other_headers *ohdr, | 
					
						
							|  |  |  | 				     void *data, | 
					
						
							|  |  |  | 				     struct ipath_qp *qp, | 
					
						
							|  |  |  | 				     u32 opcode, | 
					
						
							|  |  |  | 				     u32 psn, | 
					
						
							|  |  |  | 				     int diff, | 
					
						
							|  |  |  | 				     int header_in_data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	struct ipath_ack_entry *e; | 
					
						
							|  |  |  | 	u8 i, prev; | 
					
						
							|  |  |  | 	int old_req; | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (diff > 0) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Packet sequence error. | 
					
						
							|  |  |  | 		 * A NAK will ACK earlier sends and RDMA writes. | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		 * Don't queue the NAK if we already sent one. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (!qp->r_nak_state) { | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 			qp->r_nak_state = IB_NAK_PSN_ERROR; | 
					
						
							|  |  |  | 			/* Use the expected PSN. */ | 
					
						
							|  |  |  | 			qp->r_ack_psn = qp->r_psn; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			goto send_ack; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Handle a duplicate request.  Don't re-execute SEND, RDMA | 
					
						
							|  |  |  | 	 * write or atomic op.  Don't NAK errors, just silently drop | 
					
						
							|  |  |  | 	 * the duplicate request.  Note that r_sge, r_len, and | 
					
						
							|  |  |  | 	 * r_rcv_len may be in use so don't modify them. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * We are supposed to ACK the earliest duplicate PSN but we | 
					
						
							|  |  |  | 	 * can coalesce an outstanding duplicate ACK.  We have to | 
					
						
							|  |  |  | 	 * send the earliest so that RDMA reads can be restarted at | 
					
						
							|  |  |  | 	 * the requester's expected PSN. | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	 * | 
					
						
							|  |  |  | 	 * First, find where this duplicate PSN falls within the | 
					
						
							|  |  |  | 	 * ACKs previously sent. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	psn &= IPATH_PSN_MASK; | 
					
						
							|  |  |  | 	e = NULL; | 
					
						
							|  |  |  | 	old_req = 1; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	/* Double check we can process this now that we hold the s_lock. */ | 
					
						
							|  |  |  | 	if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) | 
					
						
							|  |  |  | 		goto unlock_done; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	for (i = qp->r_head_ack_queue; ; i = prev) { | 
					
						
							|  |  |  | 		if (i == qp->s_tail_ack_queue) | 
					
						
							|  |  |  | 			old_req = 0; | 
					
						
							|  |  |  | 		if (i) | 
					
						
							|  |  |  | 			prev = i - 1; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			prev = IPATH_MAX_RDMA_ATOMIC; | 
					
						
							|  |  |  | 		if (prev == qp->r_head_ack_queue) { | 
					
						
							|  |  |  | 			e = NULL; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		e = &qp->s_ack_queue[prev]; | 
					
						
							|  |  |  | 		if (!e->opcode) { | 
					
						
							|  |  |  | 			e = NULL; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		if (ipath_cmp24(psn, e->psn) >= 0) { | 
					
						
							|  |  |  | 			if (prev == qp->s_tail_ack_queue) | 
					
						
							|  |  |  | 				old_req = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:42 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	switch (opcode) { | 
					
						
							|  |  |  | 	case OP(RDMA_READ_REQUEST): { | 
					
						
							|  |  |  | 		struct ib_reth *reth; | 
					
						
							|  |  |  | 		u32 offset; | 
					
						
							|  |  |  | 		u32 len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * If we didn't find the RDMA read request in the ack queue, | 
					
						
							|  |  |  | 		 * or the send tasklet is already backed up to send an | 
					
						
							|  |  |  | 		 * earlier entry, we can ignore this request. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req) | 
					
						
							|  |  |  | 			goto unlock_done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/* RETH comes after BTH */ | 
					
						
							|  |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			reth = &ohdr->u.rc.reth; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			reth = (struct ib_reth *)data; | 
					
						
							|  |  |  | 			data += sizeof(*reth); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		 * Address range must be a subset of the original | 
					
						
							|  |  |  | 		 * request and start on pmtu boundaries. | 
					
						
							|  |  |  | 		 * We reuse the old ack_queue slot since the requester | 
					
						
							|  |  |  | 		 * should not back up and request an earlier PSN for the | 
					
						
							|  |  |  | 		 * same request. | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		offset = ((psn - e->psn) & IPATH_PSN_MASK) * | 
					
						
							|  |  |  | 			ib_mtu_enum_to_int(qp->path_mtu); | 
					
						
							|  |  |  | 		len = be32_to_cpu(reth->length); | 
					
						
							|  |  |  | 		if (unlikely(offset + len > e->rdma_sge.sge.sge_length)) | 
					
						
							|  |  |  | 			goto unlock_done; | 
					
						
							|  |  |  | 		if (len != 0) { | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			u32 rkey = be32_to_cpu(reth->rkey); | 
					
						
							|  |  |  | 			u64 vaddr = be64_to_cpu(reth->vaddr); | 
					
						
							|  |  |  | 			int ok; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			ok = ipath_rkey_ok(qp, &e->rdma_sge, | 
					
						
							|  |  |  | 					   len, vaddr, rkey, | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 					   IB_ACCESS_REMOTE_READ); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			if (unlikely(!ok)) | 
					
						
							|  |  |  | 				goto unlock_done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			e->rdma_sge.sg_list = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.num_sge = 0; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.mr = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.vaddr = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.length = 0; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.sge_length = 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e->psn = psn; | 
					
						
							|  |  |  | 		qp->s_ack_state = OP(ACKNOWLEDGE); | 
					
						
							|  |  |  | 		qp->s_tail_ack_queue = prev; | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	case OP(COMPARE_SWAP): | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	case OP(FETCH_ADD): { | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/*
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		 * If we didn't find the atomic request in the ack queue | 
					
						
							|  |  |  | 		 * or the send tasklet is already backed up to send an | 
					
						
							|  |  |  | 		 * earlier entry, we can ignore this request. | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (!e || e->opcode != (u8) opcode || old_req) | 
					
						
							|  |  |  | 			goto unlock_done; | 
					
						
							|  |  |  | 		qp->s_ack_state = OP(ACKNOWLEDGE); | 
					
						
							|  |  |  | 		qp->s_tail_ack_queue = prev; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		if (old_req) | 
					
						
							|  |  |  | 			goto unlock_done; | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Resend the most recent ACK if this request is | 
					
						
							|  |  |  | 		 * after all the previous RDMA reads and atomics. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (i == qp->r_head_ack_queue) { | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 			spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			qp->r_nak_state = 0; | 
					
						
							|  |  |  | 			qp->r_ack_psn = qp->r_psn - 1; | 
					
						
							|  |  |  | 			goto send_ack; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-07-25 11:08:28 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Try to send a simple ACK to work around a Mellanox bug | 
					
						
							|  |  |  | 		 * which doesn't accept a RDMA read response or atomic | 
					
						
							|  |  |  | 		 * response as an ACK for earlier SENDs or RDMA writes. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (qp->r_head_ack_queue == qp->s_tail_ack_queue && | 
					
						
							|  |  |  | 		    !(qp->s_flags & IPATH_S_ACK_PENDING) && | 
					
						
							|  |  |  | 		    qp->s_ack_state == OP(ACKNOWLEDGE)) { | 
					
						
							|  |  |  | 			spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							|  |  |  | 			qp->r_nak_state = 0; | 
					
						
							|  |  |  | 			qp->r_ack_psn = qp->s_ack_queue[i].psn - 1; | 
					
						
							|  |  |  | 			goto send_ack; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Resend the RDMA read or atomic op which | 
					
						
							|  |  |  | 		 * ACKs this duplicate request. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		qp->s_ack_state = OP(ACKNOWLEDGE); | 
					
						
							|  |  |  | 		qp->s_tail_ack_queue = i; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	qp->r_nak_state = 0; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | unlock_done: | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | done: | 
					
						
							|  |  |  | 	return 1; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | send_ack: | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err) | 
					
						
							| 
									
										
										
										
											2006-09-28 09:00:14 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2007-08-25 16:45:03 -07:00
										 |  |  | 	int lastwqe; | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2007-08-25 16:45:03 -07:00
										 |  |  | 	lastwqe = ipath_error_qp(qp, err); | 
					
						
							| 
									
										
										
										
											2007-04-27 11:11:11 -07:00
										 |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2007-08-25 16:45:03 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (lastwqe) { | 
					
						
							|  |  |  | 		struct ib_event ev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ev.device = qp->ibqp.device; | 
					
						
							|  |  |  | 		ev.element.qp = &qp->ibqp; | 
					
						
							|  |  |  | 		ev.event = IB_EVENT_QP_LAST_WQE_REACHED; | 
					
						
							|  |  |  | 		qp->ibqp.event_handler(&ev, qp->ibqp.qp_context); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-09-28 09:00:14 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | static inline void ipath_update_ack_queue(struct ipath_qp *qp, unsigned n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned next; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	next = n + 1; | 
					
						
							|  |  |  | 	if (next > IPATH_MAX_RDMA_ATOMIC) | 
					
						
							|  |  |  | 		next = 0; | 
					
						
							|  |  |  | 	if (n == qp->s_tail_ack_queue) { | 
					
						
							|  |  |  | 		qp->s_tail_ack_queue = next; | 
					
						
							|  |  |  | 		qp->s_ack_state = OP(ACKNOWLEDGE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * ipath_rc_rcv - process an incoming RC packet | 
					
						
							|  |  |  |  * @dev: the device this packet came in on | 
					
						
							|  |  |  |  * @hdr: the header of this packet | 
					
						
							|  |  |  |  * @has_grh: true if the header has a GRH | 
					
						
							|  |  |  |  * @data: the packet data | 
					
						
							|  |  |  |  * @tlen: the packet length | 
					
						
							|  |  |  |  * @qp: the QP for this packet | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is called from ipath_qp_rcv() to process an incoming RC packet | 
					
						
							|  |  |  |  * for the given QP. | 
					
						
							|  |  |  |  * Called at interrupt level. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, | 
					
						
							|  |  |  | 		  int has_grh, void *data, u32 tlen, struct ipath_qp *qp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ipath_other_headers *ohdr; | 
					
						
							|  |  |  | 	u32 opcode; | 
					
						
							|  |  |  | 	u32 hdrsize; | 
					
						
							|  |  |  | 	u32 psn; | 
					
						
							|  |  |  | 	u32 pad; | 
					
						
							|  |  |  | 	struct ib_wc wc; | 
					
						
							|  |  |  | 	u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); | 
					
						
							|  |  |  | 	int diff; | 
					
						
							|  |  |  | 	struct ib_reth *reth; | 
					
						
							|  |  |  | 	int header_in_data; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-28 09:00:06 -07:00
										 |  |  | 	/* Validate the SLID. See Ch. 9.6.1.5 */ | 
					
						
							|  |  |  | 	if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid)) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* Check for GRH */ | 
					
						
							|  |  |  | 	if (!has_grh) { | 
					
						
							|  |  |  | 		ohdr = &hdr->u.oth; | 
					
						
							|  |  |  | 		hdrsize = 8 + 12;	/* LRH + BTH */ | 
					
						
							|  |  |  | 		psn = be32_to_cpu(ohdr->bth[2]); | 
					
						
							|  |  |  | 		header_in_data = 0; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ohdr = &hdr->u.l.oth; | 
					
						
							|  |  |  | 		hdrsize = 8 + 40 + 12;	/* LRH + GRH + BTH */ | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * The header with GRH is 60 bytes and the core driver sets | 
					
						
							|  |  |  | 		 * the eager header buffer size to 56 bytes so the last 4 | 
					
						
							|  |  |  | 		 * bytes of the BTH header (PSN) is in the data buffer. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-08-25 11:24:32 -07:00
										 |  |  | 		header_in_data = dev->dd->ipath_rcvhdrentsize == 16; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (header_in_data) { | 
					
						
							|  |  |  | 			psn = be32_to_cpu(((__be32 *) data)[0]); | 
					
						
							|  |  |  | 			data += sizeof(__be32); | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			psn = be32_to_cpu(ohdr->bth[2]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Process responses (ACKs) before anything else.  Note that the | 
					
						
							|  |  |  | 	 * packet sequence number will be for something in the send work | 
					
						
							|  |  |  | 	 * queue rather than the expected receive packet sequence number. | 
					
						
							|  |  |  | 	 * In other words, this QP is the requester. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2006-07-01 04:35:50 -07:00
										 |  |  | 	opcode = be32_to_cpu(ohdr->bth[0]) >> 24; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) && | 
					
						
							|  |  |  | 	    opcode <= OP(ATOMIC_ACKNOWLEDGE)) { | 
					
						
							|  |  |  | 		ipath_rc_rcv_resp(dev, ohdr, data, tlen, qp, opcode, psn, | 
					
						
							|  |  |  | 				  hdrsize, pmtu, header_in_data); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Compute 24 bits worth of difference. */ | 
					
						
							|  |  |  | 	diff = ipath_cmp24(psn, qp->r_psn); | 
					
						
							|  |  |  | 	if (unlikely(diff)) { | 
					
						
							|  |  |  | 		if (ipath_rc_rcv_error(dev, ohdr, data, qp, opcode, | 
					
						
							|  |  |  | 				       psn, diff, header_in_data)) | 
					
						
							|  |  |  | 			goto done; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		goto send_ack; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check for opcode sequence errors. */ | 
					
						
							|  |  |  | 	switch (qp->r_state) { | 
					
						
							|  |  |  | 	case OP(SEND_FIRST): | 
					
						
							|  |  |  | 	case OP(SEND_MIDDLE): | 
					
						
							|  |  |  | 		if (opcode == OP(SEND_MIDDLE) || | 
					
						
							|  |  |  | 		    opcode == OP(SEND_LAST) || | 
					
						
							|  |  |  | 		    opcode == OP(SEND_LAST_WITH_IMMEDIATE)) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 		goto nack_inv; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_FIRST): | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_MIDDLE): | 
					
						
							|  |  |  | 		if (opcode == OP(RDMA_WRITE_MIDDLE) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_LAST) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		goto nack_inv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		if (opcode == OP(SEND_MIDDLE) || | 
					
						
							|  |  |  | 		    opcode == OP(SEND_LAST) || | 
					
						
							|  |  |  | 		    opcode == OP(SEND_LAST_WITH_IMMEDIATE) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_MIDDLE) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_LAST) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE)) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Note that it is up to the requester to not send a new | 
					
						
							|  |  |  | 		 * RDMA read or atomic operation before receiving an ACK | 
					
						
							|  |  |  | 		 * for the previous operation. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	memset(&wc, 0, sizeof wc); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* OK, process the packet. */ | 
					
						
							|  |  |  | 	switch (opcode) { | 
					
						
							|  |  |  | 	case OP(SEND_FIRST): | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 		if (!ipath_get_rwqe(qp, 0)) | 
					
						
							|  |  |  | 			goto rnr_nak; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		qp->r_rcv_len = 0; | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(SEND_MIDDLE): | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_MIDDLE): | 
					
						
							|  |  |  | 	send_middle: | 
					
						
							|  |  |  | 		/* Check for invalid length PMTU or posted rwqe len. */ | 
					
						
							|  |  |  | 		if (unlikely(tlen != (hdrsize + pmtu + 4))) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							|  |  |  | 		qp->r_rcv_len += pmtu; | 
					
						
							|  |  |  | 		if (unlikely(qp->r_rcv_len > qp->r_len)) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							|  |  |  | 		ipath_copy_sge(&qp->r_sge, data, pmtu); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): | 
					
						
							|  |  |  | 		/* consume RWQE */ | 
					
						
							|  |  |  | 		if (!ipath_get_rwqe(qp, 1)) | 
					
						
							|  |  |  | 			goto rnr_nak; | 
					
						
							|  |  |  | 		goto send_last_imm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(SEND_ONLY): | 
					
						
							|  |  |  | 	case OP(SEND_ONLY_WITH_IMMEDIATE): | 
					
						
							|  |  |  | 		if (!ipath_get_rwqe(qp, 0)) | 
					
						
							|  |  |  | 			goto rnr_nak; | 
					
						
							|  |  |  | 		qp->r_rcv_len = 0; | 
					
						
							|  |  |  | 		if (opcode == OP(SEND_ONLY)) | 
					
						
							|  |  |  | 			goto send_last; | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(SEND_LAST_WITH_IMMEDIATE): | 
					
						
							|  |  |  | 	send_last_imm: | 
					
						
							|  |  |  | 		if (header_in_data) { | 
					
						
							| 
									
										
										
										
											2008-07-14 23:48:45 -07:00
										 |  |  | 			wc.ex.imm_data = *(__be32 *) data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			data += sizeof(__be32); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Immediate data comes after BTH */ | 
					
						
							| 
									
										
										
										
											2008-07-14 23:48:45 -07:00
										 |  |  | 			wc.ex.imm_data = ohdr->u.imm_data; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		hdrsize += 4; | 
					
						
							|  |  |  | 		wc.wc_flags = IB_WC_WITH_IMM; | 
					
						
							|  |  |  | 		/* FALLTHROUGH */ | 
					
						
							|  |  |  | 	case OP(SEND_LAST): | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_LAST): | 
					
						
							|  |  |  | 	send_last: | 
					
						
							|  |  |  | 		/* Get the number of bytes the message was padded by. */ | 
					
						
							|  |  |  | 		pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; | 
					
						
							|  |  |  | 		/* Check for invalid length. */ | 
					
						
							|  |  |  | 		/* XXX LAST len should be >= 1 */ | 
					
						
							|  |  |  | 		if (unlikely(tlen < (hdrsize + pad + 4))) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							|  |  |  | 		/* Don't count the CRC. */ | 
					
						
							|  |  |  | 		tlen -= (hdrsize + pad + 4); | 
					
						
							|  |  |  | 		wc.byte_len = tlen + qp->r_rcv_len; | 
					
						
							|  |  |  | 		if (unlikely(wc.byte_len > qp->r_len)) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							|  |  |  | 		ipath_copy_sge(&qp->r_sge, data, tlen); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		qp->r_msn++; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		if (!test_and_clear_bit(IPATH_R_WRID_VALID, &qp->r_aflags)) | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		wc.wr_id = qp->r_wr_id; | 
					
						
							|  |  |  | 		wc.status = IB_WC_SUCCESS; | 
					
						
							| 
									
										
										
										
											2008-05-07 10:58:50 -07:00
										 |  |  | 		if (opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE) || | 
					
						
							|  |  |  | 		    opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) | 
					
						
							|  |  |  | 			wc.opcode = IB_WC_RECV_RDMA_WITH_IMM; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			wc.opcode = IB_WC_RECV; | 
					
						
							| 
									
										
										
										
											2006-12-31 21:09:42 +02:00
										 |  |  | 		wc.qp = &qp->ibqp; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		wc.src_qp = qp->remote_qpn; | 
					
						
							|  |  |  | 		wc.slid = qp->remote_ah_attr.dlid; | 
					
						
							|  |  |  | 		wc.sl = qp->remote_ah_attr.sl; | 
					
						
							|  |  |  | 		/* Signal completion event if the solicited bit is set. */ | 
					
						
							|  |  |  | 		ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, | 
					
						
							|  |  |  | 			       (ohdr->bth[0] & | 
					
						
							| 
									
										
										
										
											2009-01-17 17:11:57 -08:00
										 |  |  | 				cpu_to_be32(1 << 23)) != 0); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_FIRST): | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_ONLY): | 
					
						
							|  |  |  | 	case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:48 -07:00
										 |  |  | 		if (unlikely(!(qp->qp_access_flags & | 
					
						
							|  |  |  | 			       IB_ACCESS_REMOTE_WRITE))) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/* consume RWQE */ | 
					
						
							|  |  |  | 		/* RETH comes after BTH */ | 
					
						
							|  |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			reth = &ohdr->u.rc.reth; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			reth = (struct ib_reth *)data; | 
					
						
							|  |  |  | 			data += sizeof(*reth); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		hdrsize += sizeof(*reth); | 
					
						
							|  |  |  | 		qp->r_len = be32_to_cpu(reth->length); | 
					
						
							|  |  |  | 		qp->r_rcv_len = 0; | 
					
						
							|  |  |  | 		if (qp->r_len != 0) { | 
					
						
							|  |  |  | 			u32 rkey = be32_to_cpu(reth->rkey); | 
					
						
							|  |  |  | 			u64 vaddr = be64_to_cpu(reth->vaddr); | 
					
						
							|  |  |  | 			int ok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Check rkey & NAK */ | 
					
						
							| 
									
										
										
										
											2006-09-28 09:00:07 -07:00
										 |  |  | 			ok = ipath_rkey_ok(qp, &qp->r_sge, | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 					   qp->r_len, vaddr, rkey, | 
					
						
							|  |  |  | 					   IB_ACCESS_REMOTE_WRITE); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 			if (unlikely(!ok)) | 
					
						
							|  |  |  | 				goto nack_acc; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			qp->r_sge.sg_list = NULL; | 
					
						
							|  |  |  | 			qp->r_sge.sge.mr = NULL; | 
					
						
							|  |  |  | 			qp->r_sge.sge.vaddr = NULL; | 
					
						
							|  |  |  | 			qp->r_sge.sge.length = 0; | 
					
						
							|  |  |  | 			qp->r_sge.sge.sge_length = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (opcode == OP(RDMA_WRITE_FIRST)) | 
					
						
							|  |  |  | 			goto send_middle; | 
					
						
							|  |  |  | 		else if (opcode == OP(RDMA_WRITE_ONLY)) | 
					
						
							|  |  |  | 			goto send_last; | 
					
						
							|  |  |  | 		if (!ipath_get_rwqe(qp, 1)) | 
					
						
							|  |  |  | 			goto rnr_nak; | 
					
						
							|  |  |  | 		goto send_last_imm; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	case OP(RDMA_READ_REQUEST): { | 
					
						
							|  |  |  | 		struct ipath_ack_entry *e; | 
					
						
							|  |  |  | 		u32 len; | 
					
						
							|  |  |  | 		u8 next; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:48 -07:00
										 |  |  | 		if (unlikely(!(qp->qp_access_flags & | 
					
						
							|  |  |  | 			       IB_ACCESS_REMOTE_READ))) | 
					
						
							|  |  |  | 			goto nack_inv; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		next = qp->r_head_ack_queue + 1; | 
					
						
							|  |  |  | 		if (next > IPATH_MAX_RDMA_ATOMIC) | 
					
						
							|  |  |  | 			next = 0; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							|  |  |  | 		/* Double check we can process this while holding the s_lock. */ | 
					
						
							|  |  |  | 		if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) | 
					
						
							|  |  |  | 			goto unlock; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 		if (unlikely(next == qp->s_tail_ack_queue)) { | 
					
						
							|  |  |  | 			if (!qp->s_ack_queue[next].sent) | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 				goto nack_inv_unlck; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 			ipath_update_ack_queue(qp, next); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e = &qp->s_ack_queue[qp->r_head_ack_queue]; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/* RETH comes after BTH */ | 
					
						
							|  |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			reth = &ohdr->u.rc.reth; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			reth = (struct ib_reth *)data; | 
					
						
							|  |  |  | 			data += sizeof(*reth); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		len = be32_to_cpu(reth->length); | 
					
						
							|  |  |  | 		if (len) { | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			u32 rkey = be32_to_cpu(reth->rkey); | 
					
						
							|  |  |  | 			u64 vaddr = be64_to_cpu(reth->vaddr); | 
					
						
							|  |  |  | 			int ok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Check rkey & NAK */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr, | 
					
						
							|  |  |  | 					   rkey, IB_ACCESS_REMOTE_READ); | 
					
						
							|  |  |  | 			if (unlikely(!ok)) | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 				goto nack_acc_unlck; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Update the next expected PSN.  We add 1 later | 
					
						
							|  |  |  | 			 * below, so only add the remainder here. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			if (len > pmtu) | 
					
						
							|  |  |  | 				qp->r_psn += (len - 1) / pmtu; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 			e->rdma_sge.sg_list = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.num_sge = 0; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.mr = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.vaddr = NULL; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.length = 0; | 
					
						
							|  |  |  | 			e->rdma_sge.sge.sge_length = 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e->opcode = opcode; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 		e->sent = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e->psn = psn; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * We need to increment the MSN here instead of when we | 
					
						
							|  |  |  | 		 * finish sending the result since a duplicate request would | 
					
						
							|  |  |  | 		 * increment it more than once. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		qp->r_msn++; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		qp->r_psn++; | 
					
						
							|  |  |  | 		qp->r_state = opcode; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		qp->r_nak_state = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->r_head_ack_queue = next; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		/* Schedule the send tasklet. */ | 
					
						
							|  |  |  | 		ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		goto unlock; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	case OP(COMPARE_SWAP): | 
					
						
							|  |  |  | 	case OP(FETCH_ADD): { | 
					
						
							|  |  |  | 		struct ib_atomic_eth *ateth; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		struct ipath_ack_entry *e; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		u64 vaddr; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		atomic64_t *maddr; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		u64 sdata; | 
					
						
							|  |  |  | 		u32 rkey; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		u8 next; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		if (unlikely(!(qp->qp_access_flags & | 
					
						
							|  |  |  | 			       IB_ACCESS_REMOTE_ATOMIC))) | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:48 -07:00
										 |  |  | 			goto nack_inv; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		next = qp->r_head_ack_queue + 1; | 
					
						
							|  |  |  | 		if (next > IPATH_MAX_RDMA_ATOMIC) | 
					
						
							|  |  |  | 			next = 0; | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		spin_lock_irqsave(&qp->s_lock, flags); | 
					
						
							|  |  |  | 		/* Double check we can process this while holding the s_lock. */ | 
					
						
							|  |  |  | 		if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) | 
					
						
							|  |  |  | 			goto unlock; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 		if (unlikely(next == qp->s_tail_ack_queue)) { | 
					
						
							|  |  |  | 			if (!qp->s_ack_queue[next].sent) | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 				goto nack_inv_unlck; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 			ipath_update_ack_queue(qp, next); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (!header_in_data) | 
					
						
							|  |  |  | 			ateth = &ohdr->u.atomic_eth; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		else | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 			ateth = (struct ib_atomic_eth *)data; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) | | 
					
						
							|  |  |  | 			be32_to_cpu(ateth->vaddr[1]); | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		if (unlikely(vaddr & (sizeof(u64) - 1))) | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			goto nack_inv_unlck; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		rkey = be32_to_cpu(ateth->rkey); | 
					
						
							|  |  |  | 		/* Check rkey & NAK */ | 
					
						
							| 
									
										
										
										
											2006-09-28 09:00:07 -07:00
										 |  |  | 		if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 					    sizeof(u64), vaddr, rkey, | 
					
						
							|  |  |  | 					    IB_ACCESS_REMOTE_ATOMIC))) | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 			goto nack_acc_unlck; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		/* Perform atomic OP and save result. */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		maddr = (atomic64_t *) qp->r_sge.sge.vaddr; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 		sdata = be64_to_cpu(ateth->swap_data); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e = &qp->s_ack_queue[qp->r_head_ack_queue]; | 
					
						
							|  |  |  | 		e->atomic_data = (opcode == OP(FETCH_ADD)) ? | 
					
						
							|  |  |  | 			(u64) atomic64_add_return(sdata, maddr) - sdata : | 
					
						
							|  |  |  | 			(u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr, | 
					
						
							|  |  |  | 				      be64_to_cpu(ateth->compare_data), | 
					
						
							|  |  |  | 				      sdata); | 
					
						
							|  |  |  | 		e->opcode = opcode; | 
					
						
							| 
									
										
										
										
											2007-06-18 14:24:44 -07:00
										 |  |  | 		e->sent = 0; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		e->psn = psn & IPATH_PSN_MASK; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		qp->r_msn++; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		qp->r_psn++; | 
					
						
							|  |  |  | 		qp->r_state = opcode; | 
					
						
							|  |  |  | 		qp->r_nak_state = 0; | 
					
						
							|  |  |  | 		qp->r_head_ack_queue = next; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		/* Schedule the send tasklet. */ | 
					
						
							|  |  |  | 		ipath_schedule_send(qp); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 		goto unlock; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 		/* NAK unknown opcodes. */ | 
					
						
							|  |  |  | 		goto nack_inv; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	qp->r_psn++; | 
					
						
							|  |  |  | 	qp->r_state = opcode; | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	qp->r_ack_psn = psn; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 	qp->r_nak_state = 0; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	/* Send an ACK if requested or required. */ | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	if (psn & (1 << 31)) | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | 		goto send_ack; | 
					
						
							|  |  |  | 	goto done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | rnr_nak: | 
					
						
							|  |  |  | 	qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer; | 
					
						
							|  |  |  | 	qp->r_ack_psn = qp->r_psn; | 
					
						
							|  |  |  | 	goto send_ack; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | nack_inv_unlck: | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | nack_inv: | 
					
						
							|  |  |  | 	ipath_rc_error(qp, IB_WC_LOC_QP_OP_ERR); | 
					
						
							|  |  |  | 	qp->r_nak_state = IB_NAK_INVALID_REQUEST; | 
					
						
							|  |  |  | 	qp->r_ack_psn = qp->r_psn; | 
					
						
							|  |  |  | 	goto send_ack; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | nack_acc_unlck: | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | nack_acc: | 
					
						
							| 
									
										
										
										
											2008-05-13 11:40:25 -07:00
										 |  |  | 	ipath_rc_error(qp, IB_WC_LOC_PROT_ERR); | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; | 
					
						
							|  |  |  | 	qp->r_ack_psn = qp->r_psn; | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | send_ack: | 
					
						
							| 
									
										
										
										
											2007-03-15 14:44:51 -07:00
										 |  |  | 	send_rc_ack(qp); | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | 	goto done; | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-05-13 11:41:29 -07:00
										 |  |  | unlock: | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&qp->s_lock, flags); | 
					
						
							| 
									
										
										
										
											2006-07-01 04:36:10 -07:00
										 |  |  | done: | 
					
						
							| 
									
										
										
										
											2006-03-29 15:23:35 -08:00
										 |  |  | 	return; | 
					
						
							|  |  |  | } |