]>
Commit | Line | Data |
---|---|---|
f94b533d TT |
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 <linux/slab.h> | |
34 | #include <linux/spinlock.h> | |
35 | ||
36 | #include "c2_vq.h" | |
37 | #include "c2_provider.h" | |
38 | ||
39 | /* | |
40 | * Verbs Request Objects: | |
41 | * | |
42 | * VQ Request Objects are allocated by the kernel verbs handlers. | |
43 | * They contain a wait object, a refcnt, an atomic bool indicating that the | |
44 | * adapter has replied, and a copy of the verb reply work request. | |
45 | * A pointer to the VQ Request Object is passed down in the context | |
46 | * field of the work request message, and reflected back by the adapter | |
47 | * in the verbs reply message. The function handle_vq() in the interrupt | |
48 | * path will use this pointer to: | |
49 | * 1) append a copy of the verbs reply message | |
50 | * 2) mark that the reply is ready | |
51 | * 3) wake up the kernel verbs handler blocked awaiting the reply. | |
52 | * | |
53 | * | |
54 | * The kernel verbs handlers do a "get" to put a 2nd reference on the | |
55 | * VQ Request object. If the kernel verbs handler exits before the adapter | |
56 | * can respond, this extra reference will keep the VQ Request object around | |
57 | * until the adapter's reply can be processed. The reason we need this is | |
58 | * because a pointer to this object is stuffed into the context field of | |
59 | * the verbs work request message, and reflected back in the reply message. | |
60 | * It is used in the interrupt handler (handle_vq()) to wake up the appropriate | |
61 | * kernel verb handler that is blocked awaiting the verb reply. | |
62 | * So handle_vq() will do a "put" on the object when it's done accessing it. | |
63 | * NOTE: If we guarantee that the kernel verb handler will never bail before | |
64 | * getting the reply, then we don't need these refcnts. | |
65 | * | |
66 | * | |
67 | * VQ Request objects are freed by the kernel verbs handlers only | |
68 | * after the verb has been processed, or when the adapter fails and | |
69 | * does not reply. | |
70 | * | |
71 | * | |
72 | * Verbs Reply Buffers: | |
73 | * | |
74 | * VQ Reply bufs are local host memory copies of a | |
75 | * outstanding Verb Request reply | |
76 | * message. The are always allocated by the kernel verbs handlers, and _may_ be | |
77 | * freed by either the kernel verbs handler -or- the interrupt handler. The | |
78 | * kernel verbs handler _must_ free the repbuf, then free the vq request object | |
79 | * in that order. | |
80 | */ | |
81 | ||
82 | int vq_init(struct c2_dev *c2dev) | |
83 | { | |
84 | sprintf(c2dev->vq_cache_name, "c2-vq:dev%c", | |
85 | (char) ('0' + c2dev->devnum)); | |
86 | c2dev->host_msg_cache = | |
87 | kmem_cache_create(c2dev->vq_cache_name, c2dev->rep_vq.msg_size, 0, | |
20c2df83 | 88 | SLAB_HWCACHE_ALIGN, NULL); |
f94b533d TT |
89 | if (c2dev->host_msg_cache == NULL) { |
90 | return -ENOMEM; | |
91 | } | |
92 | return 0; | |
93 | } | |
94 | ||
95 | void vq_term(struct c2_dev *c2dev) | |
96 | { | |
97 | kmem_cache_destroy(c2dev->host_msg_cache); | |
98 | } | |
99 | ||
100 | /* vq_req_alloc - allocate a VQ Request Object and initialize it. | |
101 | * The refcnt is set to 1. | |
102 | */ | |
103 | struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev) | |
104 | { | |
105 | struct c2_vq_req *r; | |
106 | ||
107 | r = kmalloc(sizeof(struct c2_vq_req), GFP_KERNEL); | |
108 | if (r) { | |
109 | init_waitqueue_head(&r->wait_object); | |
110 | r->reply_msg = (u64) NULL; | |
111 | r->event = 0; | |
112 | r->cm_id = NULL; | |
113 | r->qp = NULL; | |
114 | atomic_set(&r->refcnt, 1); | |
115 | atomic_set(&r->reply_ready, 0); | |
116 | } | |
117 | return r; | |
118 | } | |
119 | ||
120 | ||
121 | /* vq_req_free - free the VQ Request Object. It is assumed the verbs handler | |
122 | * has already free the VQ Reply Buffer if it existed. | |
123 | */ | |
124 | void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *r) | |
125 | { | |
126 | r->reply_msg = (u64) NULL; | |
127 | if (atomic_dec_and_test(&r->refcnt)) { | |
128 | kfree(r); | |
129 | } | |
130 | } | |
131 | ||
132 | /* vq_req_get - reference a VQ Request Object. Done | |
133 | * only in the kernel verbs handlers. | |
134 | */ | |
135 | void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *r) | |
136 | { | |
137 | atomic_inc(&r->refcnt); | |
138 | } | |
139 | ||
140 | ||
141 | /* vq_req_put - dereference and potentially free a VQ Request Object. | |
142 | * | |
143 | * This is only called by handle_vq() on the | |
144 | * interrupt when it is done processing | |
145 | * a verb reply message. If the associated | |
146 | * kernel verbs handler has already bailed, | |
147 | * then this put will actually free the VQ | |
148 | * Request object _and_ the VQ Reply Buffer | |
149 | * if it exists. | |
150 | */ | |
151 | void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *r) | |
152 | { | |
153 | if (atomic_dec_and_test(&r->refcnt)) { | |
154 | if (r->reply_msg != (u64) NULL) | |
155 | vq_repbuf_free(c2dev, | |
156 | (void *) (unsigned long) r->reply_msg); | |
157 | kfree(r); | |
158 | } | |
159 | } | |
160 | ||
161 | ||
162 | /* | |
163 | * vq_repbuf_alloc - allocate a VQ Reply Buffer. | |
164 | */ | |
165 | void *vq_repbuf_alloc(struct c2_dev *c2dev) | |
166 | { | |
54e6ecb2 | 167 | return kmem_cache_alloc(c2dev->host_msg_cache, GFP_ATOMIC); |
f94b533d TT |
168 | } |
169 | ||
170 | /* | |
171 | * vq_send_wr - post a verbs request message to the Verbs Request Queue. | |
172 | * If a message is not available in the MQ, then block until one is available. | |
173 | * NOTE: handle_mq() on the interrupt context will wake up threads blocked here. | |
174 | * When the adapter drains the Verbs Request Queue, | |
175 | * it inserts MQ index 0 in to the | |
176 | * adapter->host activity fifo and interrupts the host. | |
177 | */ | |
178 | int vq_send_wr(struct c2_dev *c2dev, union c2wr *wr) | |
179 | { | |
180 | void *msg; | |
181 | wait_queue_t __wait; | |
182 | ||
183 | /* | |
184 | * grab adapter vq lock | |
185 | */ | |
186 | spin_lock(&c2dev->vqlock); | |
187 | ||
188 | /* | |
189 | * allocate msg | |
190 | */ | |
191 | msg = c2_mq_alloc(&c2dev->req_vq); | |
192 | ||
193 | /* | |
194 | * If we cannot get a msg, then we'll wait | |
195 | * When a messages are available, the int handler will wake_up() | |
196 | * any waiters. | |
197 | */ | |
198 | while (msg == NULL) { | |
199 | pr_debug("%s:%d no available msg in VQ, waiting...\n", | |
200 | __FUNCTION__, __LINE__); | |
201 | init_waitqueue_entry(&__wait, current); | |
202 | add_wait_queue(&c2dev->req_vq_wo, &__wait); | |
203 | spin_unlock(&c2dev->vqlock); | |
204 | for (;;) { | |
205 | set_current_state(TASK_INTERRUPTIBLE); | |
206 | if (!c2_mq_full(&c2dev->req_vq)) { | |
207 | break; | |
208 | } | |
209 | if (!signal_pending(current)) { | |
210 | schedule_timeout(1 * HZ); /* 1 second... */ | |
211 | continue; | |
212 | } | |
213 | set_current_state(TASK_RUNNING); | |
214 | remove_wait_queue(&c2dev->req_vq_wo, &__wait); | |
215 | return -EINTR; | |
216 | } | |
217 | set_current_state(TASK_RUNNING); | |
218 | remove_wait_queue(&c2dev->req_vq_wo, &__wait); | |
219 | spin_lock(&c2dev->vqlock); | |
220 | msg = c2_mq_alloc(&c2dev->req_vq); | |
221 | } | |
222 | ||
223 | /* | |
224 | * copy wr into adapter msg | |
225 | */ | |
226 | memcpy(msg, wr, c2dev->req_vq.msg_size); | |
227 | ||
228 | /* | |
229 | * post msg | |
230 | */ | |
231 | c2_mq_produce(&c2dev->req_vq); | |
232 | ||
233 | /* | |
234 | * release adapter vq lock | |
235 | */ | |
236 | spin_unlock(&c2dev->vqlock); | |
237 | return 0; | |
238 | } | |
239 | ||
240 | ||
241 | /* | |
242 | * vq_wait_for_reply - block until the adapter posts a Verb Reply Message. | |
243 | */ | |
244 | int vq_wait_for_reply(struct c2_dev *c2dev, struct c2_vq_req *req) | |
245 | { | |
246 | if (!wait_event_timeout(req->wait_object, | |
247 | atomic_read(&req->reply_ready), | |
248 | 60*HZ)) | |
249 | return -ETIMEDOUT; | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | /* | |
255 | * vq_repbuf_free - Free a Verbs Reply Buffer. | |
256 | */ | |
257 | void vq_repbuf_free(struct c2_dev *c2dev, void *reply) | |
258 | { | |
259 | kmem_cache_free(c2dev->host_msg_cache, reply); | |
260 | } |