1/* 2 * Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved. 3 * Copyright (c) 2005, 2006 PathScale, 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 34#include <linux/err.h> 35#include <linux/slab.h> 36#include <linux/vmalloc.h> 37 38#include "ipath_verbs.h" 39 40/** 41 * ipath_post_srq_receive - post a receive on a shared receive queue 42 * @ibsrq: the SRQ to post the receive on 43 * @wr: the list of work requests to post 44 * @bad_wr: the first WR to cause a problem is put here 45 * 46 * This may be called from interrupt context. 47 */ 48int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr, 49 struct ib_recv_wr **bad_wr) 50{ 51 struct ipath_srq *srq = to_isrq(ibsrq); 52 struct ipath_rwq *wq; 53 unsigned long flags; 54 int ret; 55 56 for (; wr; wr = wr->next) { 57 struct ipath_rwqe *wqe; 58 u32 next; 59 int i; 60 61 if ((unsigned) wr->num_sge > srq->rq.max_sge) { 62 *bad_wr = wr; 63 ret = -EINVAL; 64 goto bail; 65 } 66 67 spin_lock_irqsave(&srq->rq.lock, flags); 68 wq = srq->rq.wq; 69 next = wq->head + 1; 70 if (next >= srq->rq.size) 71 next = 0; 72 if (next == wq->tail) { 73 spin_unlock_irqrestore(&srq->rq.lock, flags); 74 *bad_wr = wr; 75 ret = -ENOMEM; 76 goto bail; 77 } 78 79 wqe = get_rwqe_ptr(&srq->rq, wq->head); 80 wqe->wr_id = wr->wr_id; 81 wqe->num_sge = wr->num_sge; 82 for (i = 0; i < wr->num_sge; i++) 83 wqe->sg_list[i] = wr->sg_list[i]; 84 /* Make sure queue entry is written before the head index. */ 85 smp_wmb(); 86 wq->head = next; 87 spin_unlock_irqrestore(&srq->rq.lock, flags); 88 } 89 ret = 0; 90 91bail: 92 return ret; 93} 94 95/** 96 * ipath_create_srq - create a shared receive queue 97 * @ibpd: the protection domain of the SRQ to create 98 * @srq_init_attr: the attributes of the SRQ 99 * @udata: data from libipathverbs when creating a user SRQ 100 */ 101struct ib_srq *ipath_create_srq(struct ib_pd *ibpd, 102 struct ib_srq_init_attr *srq_init_attr, 103 struct ib_udata *udata) 104{ 105 struct ipath_ibdev *dev = to_idev(ibpd->device); 106 struct ipath_srq *srq; 107 u32 sz; 108 struct ib_srq *ret; 109 110 if (srq_init_attr->srq_type != IB_SRQT_BASIC) { 111 ret = ERR_PTR(-ENOSYS); 112 goto done; 113 } 114 115 if (srq_init_attr->attr.max_wr == 0) { 116 ret = ERR_PTR(-EINVAL); 117 goto done; 118 } 119 120 if ((srq_init_attr->attr.max_sge > ib_ipath_max_srq_sges) || 121 (srq_init_attr->attr.max_wr > ib_ipath_max_srq_wrs)) { 122 ret = ERR_PTR(-EINVAL); 123 goto done; 124 } 125 126 srq = kmalloc(sizeof(*srq), GFP_KERNEL); 127 if (!srq) { 128 ret = ERR_PTR(-ENOMEM); 129 goto done; 130 } 131 132 /* 133 * Need to use vmalloc() if we want to support large #s of entries. 134 */ 135 srq->rq.size = srq_init_attr->attr.max_wr + 1; 136 srq->rq.max_sge = srq_init_attr->attr.max_sge; 137 sz = sizeof(struct ib_sge) * srq->rq.max_sge + 138 sizeof(struct ipath_rwqe); 139 srq->rq.wq = vmalloc_user(sizeof(struct ipath_rwq) + srq->rq.size * sz); 140 if (!srq->rq.wq) { 141 ret = ERR_PTR(-ENOMEM); 142 goto bail_srq; 143 } 144 145 /* 146 * Return the address of the RWQ as the offset to mmap. 147 * See ipath_mmap() for details. 148 */ 149 if (udata && udata->outlen >= sizeof(__u64)) { 150 int err; 151 u32 s = sizeof(struct ipath_rwq) + srq->rq.size * sz; 152 153 srq->ip = 154 ipath_create_mmap_info(dev, s, 155 ibpd->uobject->context, 156 srq->rq.wq); 157 if (!srq->ip) { 158 ret = ERR_PTR(-ENOMEM); 159 goto bail_wq; 160 } 161 162 err = ib_copy_to_udata(udata, &srq->ip->offset, 163 sizeof(srq->ip->offset)); 164 if (err) { 165 ret = ERR_PTR(err); 166 goto bail_ip; 167 } 168 } else 169 srq->ip = NULL; 170 171 /* 172 * ib_create_srq() will initialize srq->ibsrq. 173 */ 174 spin_lock_init(&srq->rq.lock); 175 srq->rq.wq->head = 0; 176 srq->rq.wq->tail = 0; 177 srq->limit = srq_init_attr->attr.srq_limit; 178 179 spin_lock(&dev->n_srqs_lock); 180 if (dev->n_srqs_allocated == ib_ipath_max_srqs) { 181 spin_unlock(&dev->n_srqs_lock); 182 ret = ERR_PTR(-ENOMEM); 183 goto bail_ip; 184 } 185 186 dev->n_srqs_allocated++; 187 spin_unlock(&dev->n_srqs_lock); 188 189 if (srq->ip) { 190 spin_lock_irq(&dev->pending_lock); 191 list_add(&srq->ip->pending_mmaps, &dev->pending_mmaps); 192 spin_unlock_irq(&dev->pending_lock); 193 } 194 195 ret = &srq->ibsrq; 196 goto done; 197 198bail_ip: 199 kfree(srq->ip); 200bail_wq: 201 vfree(srq->rq.wq); 202bail_srq: 203 kfree(srq); 204done: 205 return ret; 206} 207 208/** 209 * ipath_modify_srq - modify a shared receive queue 210 * @ibsrq: the SRQ to modify 211 * @attr: the new attributes of the SRQ 212 * @attr_mask: indicates which attributes to modify 213 * @udata: user data for ipathverbs.so 214 */ 215int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, 216 enum ib_srq_attr_mask attr_mask, 217 struct ib_udata *udata) 218{ 219 struct ipath_srq *srq = to_isrq(ibsrq); 220 struct ipath_rwq *wq; 221 int ret = 0; 222 223 if (attr_mask & IB_SRQ_MAX_WR) { 224 struct ipath_rwq *owq; 225 struct ipath_rwqe *p; 226 u32 sz, size, n, head, tail; 227 228 /* Check that the requested sizes are below the limits. */ 229 if ((attr->max_wr > ib_ipath_max_srq_wrs) || 230 ((attr_mask & IB_SRQ_LIMIT) ? 231 attr->srq_limit : srq->limit) > attr->max_wr) { 232 ret = -EINVAL; 233 goto bail; 234 } 235 236 sz = sizeof(struct ipath_rwqe) + 237 srq->rq.max_sge * sizeof(struct ib_sge); 238 size = attr->max_wr + 1; 239 wq = vmalloc_user(sizeof(struct ipath_rwq) + size * sz); 240 if (!wq) { 241 ret = -ENOMEM; 242 goto bail; 243 } 244 245 /* Check that we can write the offset to mmap. */ 246 if (udata && udata->inlen >= sizeof(__u64)) { 247 __u64 offset_addr; 248 __u64 offset = 0; 249 250 ret = ib_copy_from_udata(&offset_addr, udata, 251 sizeof(offset_addr)); 252 if (ret) 253 goto bail_free; 254 udata->outbuf = 255 (void __user *) (unsigned long) offset_addr; 256 ret = ib_copy_to_udata(udata, &offset, 257 sizeof(offset)); 258 if (ret) 259 goto bail_free; 260 } 261 262 spin_lock_irq(&srq->rq.lock); 263 /* 264 * validate head pointer value and compute 265 * the number of remaining WQEs. 266 */ 267 owq = srq->rq.wq; 268 head = owq->head; 269 if (head >= srq->rq.size) 270 head = 0; 271 tail = owq->tail; 272 if (tail >= srq->rq.size) 273 tail = 0; 274 n = head; 275 if (n < tail) 276 n += srq->rq.size - tail; 277 else 278 n -= tail; 279 if (size <= n) { 280 ret = -EINVAL; 281 goto bail_unlock; 282 } 283 n = 0; 284 p = wq->wq; 285 while (tail != head) { 286 struct ipath_rwqe *wqe; 287 int i; 288 289 wqe = get_rwqe_ptr(&srq->rq, tail); 290 p->wr_id = wqe->wr_id; 291 p->num_sge = wqe->num_sge; 292 for (i = 0; i < wqe->num_sge; i++) 293 p->sg_list[i] = wqe->sg_list[i]; 294 n++; 295 p = (struct ipath_rwqe *)((char *) p + sz); 296 if (++tail >= srq->rq.size) 297 tail = 0; 298 } 299 srq->rq.wq = wq; 300 srq->rq.size = size; 301 wq->head = n; 302 wq->tail = 0; 303 if (attr_mask & IB_SRQ_LIMIT) 304 srq->limit = attr->srq_limit; 305 spin_unlock_irq(&srq->rq.lock); 306 307 vfree(owq); 308 309 if (srq->ip) { 310 struct ipath_mmap_info *ip = srq->ip; 311 struct ipath_ibdev *dev = to_idev(srq->ibsrq.device); 312 u32 s = sizeof(struct ipath_rwq) + size * sz; 313 314 ipath_update_mmap_info(dev, ip, s, wq); 315 316 /* 317 * Return the offset to mmap. 318 * See ipath_mmap() for details. 319 */ 320 if (udata && udata->inlen >= sizeof(__u64)) { 321 ret = ib_copy_to_udata(udata, &ip->offset, 322 sizeof(ip->offset)); 323 if (ret) 324 goto bail; 325 } 326 327 spin_lock_irq(&dev->pending_lock); 328 if (list_empty(&ip->pending_mmaps)) 329 list_add(&ip->pending_mmaps, 330 &dev->pending_mmaps); 331 spin_unlock_irq(&dev->pending_lock); 332 } 333 } else if (attr_mask & IB_SRQ_LIMIT) { 334 spin_lock_irq(&srq->rq.lock); 335 if (attr->srq_limit >= srq->rq.size) 336 ret = -EINVAL; 337 else 338 srq->limit = attr->srq_limit; 339 spin_unlock_irq(&srq->rq.lock); 340 } 341 goto bail; 342 343bail_unlock: 344 spin_unlock_irq(&srq->rq.lock); 345bail_free: 346 vfree(wq); 347bail: 348 return ret; 349} 350 351int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) 352{ 353 struct ipath_srq *srq = to_isrq(ibsrq); 354 355 attr->max_wr = srq->rq.size - 1; 356 attr->max_sge = srq->rq.max_sge; 357 attr->srq_limit = srq->limit; 358 return 0; 359} 360 361/** 362 * ipath_destroy_srq - destroy a shared receive queue 363 * @ibsrq: the SRQ to destroy 364 */ 365int ipath_destroy_srq(struct ib_srq *ibsrq) 366{ 367 struct ipath_srq *srq = to_isrq(ibsrq); 368 struct ipath_ibdev *dev = to_idev(ibsrq->device); 369 370 spin_lock(&dev->n_srqs_lock); 371 dev->n_srqs_allocated--; 372 spin_unlock(&dev->n_srqs_lock); 373 if (srq->ip) 374 kref_put(&srq->ip->ref, ipath_release_mmap_info); 375 else 376 vfree(srq->rq.wq); 377 kfree(srq); 378 379 return 0; 380} 381