diff options
Diffstat (limited to 'drivers/infiniband/hw/amso1100/c2_intr.c')
-rw-r--r-- | drivers/infiniband/hw/amso1100/c2_intr.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/amso1100/c2_intr.c b/drivers/infiniband/hw/amso1100/c2_intr.c new file mode 100644 index 000000000000..0d0bc33ca30a --- /dev/null +++ b/drivers/infiniband/hw/amso1100/c2_intr.c | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Ammasso, Inc. All rights reserved. | ||
3 | * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | */ | ||
33 | #include "c2.h" | ||
34 | #include <rdma/iw_cm.h> | ||
35 | #include "c2_vq.h" | ||
36 | |||
37 | static void handle_mq(struct c2_dev *c2dev, u32 index); | ||
38 | static void handle_vq(struct c2_dev *c2dev, u32 mq_index); | ||
39 | |||
40 | /* | ||
41 | * Handle RNIC interrupts | ||
42 | */ | ||
43 | void c2_rnic_interrupt(struct c2_dev *c2dev) | ||
44 | { | ||
45 | unsigned int mq_index; | ||
46 | |||
47 | while (c2dev->hints_read != be16_to_cpu(*c2dev->hint_count)) { | ||
48 | mq_index = readl(c2dev->regs + PCI_BAR0_HOST_HINT); | ||
49 | if (mq_index & 0x80000000) { | ||
50 | break; | ||
51 | } | ||
52 | |||
53 | c2dev->hints_read++; | ||
54 | handle_mq(c2dev, mq_index); | ||
55 | } | ||
56 | |||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Top level MQ handler | ||
61 | */ | ||
62 | static void handle_mq(struct c2_dev *c2dev, u32 mq_index) | ||
63 | { | ||
64 | if (c2dev->qptr_array[mq_index] == NULL) { | ||
65 | pr_debug(KERN_INFO "handle_mq: stray activity for mq_index=%d\n", | ||
66 | mq_index); | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | switch (mq_index) { | ||
71 | case (0): | ||
72 | /* | ||
73 | * An index of 0 in the activity queue | ||
74 | * indicates the req vq now has messages | ||
75 | * available... | ||
76 | * | ||
77 | * Wake up any waiters waiting on req VQ | ||
78 | * message availability. | ||
79 | */ | ||
80 | wake_up(&c2dev->req_vq_wo); | ||
81 | break; | ||
82 | case (1): | ||
83 | handle_vq(c2dev, mq_index); | ||
84 | break; | ||
85 | case (2): | ||
86 | /* We have to purge the VQ in case there are pending | ||
87 | * accept reply requests that would result in the | ||
88 | * generation of an ESTABLISHED event. If we don't | ||
89 | * generate these first, a CLOSE event could end up | ||
90 | * being delivered before the ESTABLISHED event. | ||
91 | */ | ||
92 | handle_vq(c2dev, 1); | ||
93 | |||
94 | c2_ae_event(c2dev, mq_index); | ||
95 | break; | ||
96 | default: | ||
97 | /* There is no event synchronization between CQ events | ||
98 | * and AE or CM events. In fact, CQE could be | ||
99 | * delivered for all of the I/O up to and including the | ||
100 | * FLUSH for a peer disconenct prior to the ESTABLISHED | ||
101 | * event being delivered to the app. The reason for this | ||
102 | * is that CM events are delivered on a thread, while AE | ||
103 | * and CM events are delivered on interrupt context. | ||
104 | */ | ||
105 | c2_cq_event(c2dev, mq_index); | ||
106 | break; | ||
107 | } | ||
108 | |||
109 | return; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Handles verbs WR replies. | ||
114 | */ | ||
115 | static void handle_vq(struct c2_dev *c2dev, u32 mq_index) | ||
116 | { | ||
117 | void *adapter_msg, *reply_msg; | ||
118 | struct c2wr_hdr *host_msg; | ||
119 | struct c2wr_hdr tmp; | ||
120 | struct c2_mq *reply_vq; | ||
121 | struct c2_vq_req *req; | ||
122 | struct iw_cm_event cm_event; | ||
123 | int err; | ||
124 | |||
125 | reply_vq = (struct c2_mq *) c2dev->qptr_array[mq_index]; | ||
126 | |||
127 | /* | ||
128 | * get next msg from mq_index into adapter_msg. | ||
129 | * don't free it yet. | ||
130 | */ | ||
131 | adapter_msg = c2_mq_consume(reply_vq); | ||
132 | if (adapter_msg == NULL) { | ||
133 | return; | ||
134 | } | ||
135 | |||
136 | host_msg = vq_repbuf_alloc(c2dev); | ||
137 | |||
138 | /* | ||
139 | * If we can't get a host buffer, then we'll still | ||
140 | * wakeup the waiter, we just won't give him the msg. | ||
141 | * It is assumed the waiter will deal with this... | ||
142 | */ | ||
143 | if (!host_msg) { | ||
144 | pr_debug("handle_vq: no repbufs!\n"); | ||
145 | |||
146 | /* | ||
147 | * just copy the WR header into a local variable. | ||
148 | * this allows us to still demux on the context | ||
149 | */ | ||
150 | host_msg = &tmp; | ||
151 | memcpy(host_msg, adapter_msg, sizeof(tmp)); | ||
152 | reply_msg = NULL; | ||
153 | } else { | ||
154 | memcpy(host_msg, adapter_msg, reply_vq->msg_size); | ||
155 | reply_msg = host_msg; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * consume the msg from the MQ | ||
160 | */ | ||
161 | c2_mq_free(reply_vq); | ||
162 | |||
163 | /* | ||
164 | * wakeup the waiter. | ||
165 | */ | ||
166 | req = (struct c2_vq_req *) (unsigned long) host_msg->context; | ||
167 | if (req == NULL) { | ||
168 | /* | ||
169 | * We should never get here, as the adapter should | ||
170 | * never send us a reply that we're not expecting. | ||
171 | */ | ||
172 | vq_repbuf_free(c2dev, host_msg); | ||
173 | pr_debug("handle_vq: UNEXPECTEDLY got NULL req\n"); | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | err = c2_errno(reply_msg); | ||
178 | if (!err) switch (req->event) { | ||
179 | case IW_CM_EVENT_ESTABLISHED: | ||
180 | c2_set_qp_state(req->qp, | ||
181 | C2_QP_STATE_RTS); | ||
182 | case IW_CM_EVENT_CLOSE: | ||
183 | |||
184 | /* | ||
185 | * Move the QP to RTS if this is | ||
186 | * the established event | ||
187 | */ | ||
188 | cm_event.event = req->event; | ||
189 | cm_event.status = 0; | ||
190 | cm_event.local_addr = req->cm_id->local_addr; | ||
191 | cm_event.remote_addr = req->cm_id->remote_addr; | ||
192 | cm_event.private_data = NULL; | ||
193 | cm_event.private_data_len = 0; | ||
194 | req->cm_id->event_handler(req->cm_id, &cm_event); | ||
195 | break; | ||
196 | default: | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | req->reply_msg = (u64) (unsigned long) (reply_msg); | ||
201 | atomic_set(&req->reply_ready, 1); | ||
202 | wake_up(&req->wait_object); | ||
203 | |||
204 | /* | ||
205 | * If the request was cancelled, then this put will | ||
206 | * free the vq_req memory...and reply_msg!!! | ||
207 | */ | ||
208 | vq_req_put(c2dev, req); | ||
209 | } | ||