Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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, |
Paul Mundt | 20c2df8 | 2007-07-20 10:11:58 +0900 | [diff] [blame] | 88 | SLAB_HWCACHE_ALIGN, NULL); |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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); |
Ralf Thielow | f9a4f6d | 2011-01-23 16:30:03 +0000 | [diff] [blame] | 110 | r->reply_msg = 0; |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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 | { |
Ralf Thielow | f9a4f6d | 2011-01-23 16:30:03 +0000 | [diff] [blame] | 126 | r->reply_msg = 0; |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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)) { |
Ralf Thielow | f9a4f6d | 2011-01-23 16:30:03 +0000 | [diff] [blame] | 154 | if (r->reply_msg != 0) |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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 | { |
Christoph Lameter | 54e6ecb | 2006-12-06 20:33:16 -0800 | [diff] [blame] | 167 | return kmem_cache_alloc(c2dev->host_msg_cache, GFP_ATOMIC); |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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", |
Harvey Harrison | 3371836 | 2008-04-16 21:01:10 -0700 | [diff] [blame] | 200 | __func__, __LINE__); |
Tom Tucker | f94b533 | 2006-09-22 15:22:48 -0700 | [diff] [blame] | 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 | } |