/* * 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 #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) { pr_debug(KERN_INFO "handle_mq: stray activity for mq_index=%d\n", mq_index); 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; } err = c2_errno(reply_msg); if (!err) switch (req->event) { case IW_CM_EVENT_ESTABLISHED: c2_set_qp_state(req->qp, C2_QP_STATE_RTS); 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); }