210 lines
5.5 KiB
C
210 lines
5.5 KiB
C
|
/*
|
||
|
* 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) {
|
||
|
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);
|
||
|
}
|