| 
									
										
										
										
											2006-09-22 15:22:48 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2005 Ammasso, Inc. All rights reserved. | 
					
						
							|  |  |  |  * Copyright (c) 2005 Open Grid Computing, 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include "c2.h"
 | 
					
						
							|  |  |  | #include <rdma/iw_cm.h>
 | 
					
						
							|  |  |  | #include "c2_vq.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void handle_mq(struct c2_dev *c2dev, u32 index); | 
					
						
							|  |  |  | static void handle_vq(struct c2_dev *c2dev, u32 mq_index); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Handle RNIC interrupts | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void c2_rnic_interrupt(struct c2_dev *c2dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int mq_index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (c2dev->hints_read != be16_to_cpu(*c2dev->hint_count)) { | 
					
						
							|  |  |  | 		mq_index = readl(c2dev->regs + PCI_BAR0_HOST_HINT); | 
					
						
							|  |  |  | 		if (mq_index & 0x80000000) { | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c2dev->hints_read++; | 
					
						
							|  |  |  | 		handle_mq(c2dev, mq_index); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Top level MQ handler | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void handle_mq(struct c2_dev *c2dev, u32 mq_index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (c2dev->qptr_array[mq_index] == NULL) { | 
					
						
							| 
									
										
										
										
											2010-09-11 22:10:55 -07:00
										 |  |  | 		pr_debug("handle_mq: stray activity for mq_index=%d\n", | 
					
						
							|  |  |  | 			 mq_index); | 
					
						
							| 
									
										
										
										
											2006-09-22 15:22:48 -07:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (mq_index) { | 
					
						
							|  |  |  | 	case (0): | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * An index of 0 in the activity queue | 
					
						
							|  |  |  | 		 * indicates the req vq now has messages | 
					
						
							|  |  |  | 		 * available... | 
					
						
							|  |  |  | 		 * | 
					
						
							|  |  |  | 		 * Wake up any waiters waiting on req VQ | 
					
						
							|  |  |  | 		 * message availability. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		wake_up(&c2dev->req_vq_wo); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case (1): | 
					
						
							|  |  |  | 		handle_vq(c2dev, mq_index); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case (2): | 
					
						
							|  |  |  | 		/* We have to purge the VQ in case there are pending
 | 
					
						
							|  |  |  | 		 * accept reply requests that would result in the | 
					
						
							|  |  |  | 		 * generation of an ESTABLISHED event. If we don't | 
					
						
							|  |  |  | 		 * generate these first, a CLOSE event could end up | 
					
						
							|  |  |  | 		 * being delivered before the ESTABLISHED event. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		handle_vq(c2dev, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		c2_ae_event(c2dev, mq_index); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		/* There is no event synchronization between CQ events
 | 
					
						
							|  |  |  | 		 * and AE or CM events. In fact, CQE could be | 
					
						
							|  |  |  | 		 * delivered for all of the I/O up to and including the | 
					
						
							|  |  |  | 		 * FLUSH for a peer disconenct prior to the ESTABLISHED | 
					
						
							|  |  |  | 		 * event being delivered to the app. The reason for this | 
					
						
							|  |  |  | 		 * is that CM events are delivered on a thread, while AE | 
					
						
							|  |  |  | 		 * and CM events are delivered on interrupt context. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		c2_cq_event(c2dev, mq_index); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Handles verbs WR replies. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void handle_vq(struct c2_dev *c2dev, u32 mq_index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *adapter_msg, *reply_msg; | 
					
						
							|  |  |  | 	struct c2wr_hdr *host_msg; | 
					
						
							|  |  |  | 	struct c2wr_hdr tmp; | 
					
						
							|  |  |  | 	struct c2_mq *reply_vq; | 
					
						
							|  |  |  | 	struct c2_vq_req *req; | 
					
						
							|  |  |  | 	struct iw_cm_event cm_event; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reply_vq = (struct c2_mq *) c2dev->qptr_array[mq_index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * get next msg from mq_index into adapter_msg. | 
					
						
							|  |  |  | 	 * don't free it yet. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	adapter_msg = c2_mq_consume(reply_vq); | 
					
						
							|  |  |  | 	if (adapter_msg == NULL) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	host_msg = vq_repbuf_alloc(c2dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If we can't get a host buffer, then we'll still | 
					
						
							|  |  |  | 	 * wakeup the waiter, we just won't give him the msg. | 
					
						
							|  |  |  | 	 * It is assumed the waiter will deal with this... | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!host_msg) { | 
					
						
							|  |  |  | 		pr_debug("handle_vq: no repbufs!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * just copy the WR header into a local variable. | 
					
						
							|  |  |  | 		 * this allows us to still demux on the context | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		host_msg = &tmp; | 
					
						
							|  |  |  | 		memcpy(host_msg, adapter_msg, sizeof(tmp)); | 
					
						
							|  |  |  | 		reply_msg = NULL; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		memcpy(host_msg, adapter_msg, reply_vq->msg_size); | 
					
						
							|  |  |  | 		reply_msg = host_msg; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * consume the msg from the MQ | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	c2_mq_free(reply_vq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * wakeup the waiter. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	req = (struct c2_vq_req *) (unsigned long) host_msg->context; | 
					
						
							|  |  |  | 	if (req == NULL) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * We should never get here, as the adapter should | 
					
						
							|  |  |  | 		 * never send us a reply that we're not expecting. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		vq_repbuf_free(c2dev, host_msg); | 
					
						
							|  |  |  | 		pr_debug("handle_vq: UNEXPECTEDLY got NULL req\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-04-16 21:09:34 -07:00
										 |  |  | 	if (reply_msg) | 
					
						
							|  |  |  | 		err = c2_errno(reply_msg); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		err = -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-09-22 15:22:48 -07:00
										 |  |  | 	if (!err) switch (req->event) { | 
					
						
							|  |  |  | 	case IW_CM_EVENT_ESTABLISHED: | 
					
						
							|  |  |  | 		c2_set_qp_state(req->qp, | 
					
						
							|  |  |  | 				C2_QP_STATE_RTS); | 
					
						
							| 
									
										
										
										
											2011-09-25 20:17:45 +05:30
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Until ird/ord negotiation via MPAv2 support is added, send | 
					
						
							|  |  |  | 		 * max supported values | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		cm_event.ird = cm_event.ord = 128; | 
					
						
							| 
									
										
										
										
											2006-09-22 15:22:48 -07:00
										 |  |  | 	case IW_CM_EVENT_CLOSE: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Move the QP to RTS if this is | 
					
						
							|  |  |  | 		 * the established event | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		cm_event.event = req->event; | 
					
						
							|  |  |  | 		cm_event.status = 0; | 
					
						
							|  |  |  | 		cm_event.local_addr = req->cm_id->local_addr; | 
					
						
							|  |  |  | 		cm_event.remote_addr = req->cm_id->remote_addr; | 
					
						
							|  |  |  | 		cm_event.private_data = NULL; | 
					
						
							|  |  |  | 		cm_event.private_data_len = 0; | 
					
						
							|  |  |  | 		req->cm_id->event_handler(req->cm_id, &cm_event); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	req->reply_msg = (u64) (unsigned long) (reply_msg); | 
					
						
							|  |  |  | 	atomic_set(&req->reply_ready, 1); | 
					
						
							|  |  |  | 	wake_up(&req->wait_object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * If the request was cancelled, then this put will | 
					
						
							|  |  |  | 	 * free the vq_req memory...and reply_msg!!! | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	vq_req_put(c2dev, req); | 
					
						
							|  |  |  | } |