1 /* 2 * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32 /* Crude resource management */ 33 #include <linux/spinlock.h> 34 #include <linux/genalloc.h> 35 #include <linux/ratelimit.h> 36 #include "iw_cxgb4.h" 37 38 static int c4iw_init_qid_table(struct c4iw_rdev *rdev) 39 { 40 u32 i; 41 42 if (c4iw_id_table_alloc(&rdev->resource.qid_table, 43 rdev->lldi.vr->qp.start, 44 rdev->lldi.vr->qp.size, 45 rdev->lldi.vr->qp.size, 0)) 46 return -ENOMEM; 47 48 for (i = rdev->lldi.vr->qp.start; 49 i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++) 50 if (!(i & rdev->qpmask)) 51 c4iw_id_free(&rdev->resource.qid_table, i); 52 return 0; 53 } 54 55 /* nr_* must be power of 2 */ 56 int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid) 57 { 58 int err = 0; 59 err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1, 60 C4IW_ID_TABLE_F_RANDOM); 61 if (err) 62 goto tpt_err; 63 err = c4iw_init_qid_table(rdev); 64 if (err) 65 goto qid_err; 66 err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0, 67 nr_pdid, 1, 0); 68 if (err) 69 goto pdid_err; 70 return 0; 71 pdid_err: 72 c4iw_id_table_free(&rdev->resource.qid_table); 73 qid_err: 74 c4iw_id_table_free(&rdev->resource.tpt_table); 75 tpt_err: 76 return -ENOMEM; 77 } 78 79 /* 80 * returns 0 if no resource available 81 */ 82 u32 c4iw_get_resource(struct c4iw_id_table *id_table) 83 { 84 u32 entry; 85 entry = c4iw_id_alloc(id_table); 86 if (entry == (u32)(-1)) 87 return 0; 88 return entry; 89 } 90 91 void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry) 92 { 93 PDBG("%s entry 0x%x\n", __func__, entry); 94 c4iw_id_free(id_table, entry); 95 } 96 97 u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) 98 { 99 struct c4iw_qid_list *entry; 100 u32 qid; 101 int i; 102 103 mutex_lock(&uctx->lock); 104 if (!list_empty(&uctx->cqids)) { 105 entry = list_entry(uctx->cqids.next, struct c4iw_qid_list, 106 entry); 107 list_del(&entry->entry); 108 qid = entry->qid; 109 kfree(entry); 110 } else { 111 qid = c4iw_get_resource(&rdev->resource.qid_table); 112 if (!qid) 113 goto out; 114 mutex_lock(&rdev->stats.lock); 115 rdev->stats.qid.cur += rdev->qpmask + 1; 116 mutex_unlock(&rdev->stats.lock); 117 for (i = qid+1; i & rdev->qpmask; i++) { 118 entry = kmalloc(sizeof *entry, GFP_KERNEL); 119 if (!entry) 120 goto out; 121 entry->qid = i; 122 list_add_tail(&entry->entry, &uctx->cqids); 123 } 124 125 /* 126 * now put the same ids on the qp list since they all 127 * map to the same db/gts page. 128 */ 129 entry = kmalloc(sizeof *entry, GFP_KERNEL); 130 if (!entry) 131 goto out; 132 entry->qid = qid; 133 list_add_tail(&entry->entry, &uctx->qpids); 134 for (i = qid+1; i & rdev->qpmask; i++) { 135 entry = kmalloc(sizeof *entry, GFP_KERNEL); 136 if (!entry) 137 goto out; 138 entry->qid = i; 139 list_add_tail(&entry->entry, &uctx->qpids); 140 } 141 } 142 out: 143 mutex_unlock(&uctx->lock); 144 PDBG("%s qid 0x%x\n", __func__, qid); 145 mutex_lock(&rdev->stats.lock); 146 if (rdev->stats.qid.cur > rdev->stats.qid.max) 147 rdev->stats.qid.max = rdev->stats.qid.cur; 148 mutex_unlock(&rdev->stats.lock); 149 return qid; 150 } 151 152 void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid, 153 struct c4iw_dev_ucontext *uctx) 154 { 155 struct c4iw_qid_list *entry; 156 157 entry = kmalloc(sizeof *entry, GFP_KERNEL); 158 if (!entry) 159 return; 160 PDBG("%s qid 0x%x\n", __func__, qid); 161 entry->qid = qid; 162 mutex_lock(&uctx->lock); 163 list_add_tail(&entry->entry, &uctx->cqids); 164 mutex_unlock(&uctx->lock); 165 } 166 167 u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) 168 { 169 struct c4iw_qid_list *entry; 170 u32 qid; 171 int i; 172 173 mutex_lock(&uctx->lock); 174 if (!list_empty(&uctx->qpids)) { 175 entry = list_entry(uctx->qpids.next, struct c4iw_qid_list, 176 entry); 177 list_del(&entry->entry); 178 qid = entry->qid; 179 kfree(entry); 180 } else { 181 qid = c4iw_get_resource(&rdev->resource.qid_table); 182 if (!qid) { 183 mutex_lock(&rdev->stats.lock); 184 rdev->stats.qid.fail++; 185 mutex_unlock(&rdev->stats.lock); 186 goto out; 187 } 188 mutex_lock(&rdev->stats.lock); 189 rdev->stats.qid.cur += rdev->qpmask + 1; 190 mutex_unlock(&rdev->stats.lock); 191 for (i = qid+1; i & rdev->qpmask; i++) { 192 entry = kmalloc(sizeof *entry, GFP_KERNEL); 193 if (!entry) 194 goto out; 195 entry->qid = i; 196 list_add_tail(&entry->entry, &uctx->qpids); 197 } 198 199 /* 200 * now put the same ids on the cq list since they all 201 * map to the same db/gts page. 202 */ 203 entry = kmalloc(sizeof *entry, GFP_KERNEL); 204 if (!entry) 205 goto out; 206 entry->qid = qid; 207 list_add_tail(&entry->entry, &uctx->cqids); 208 for (i = qid; i & rdev->qpmask; i++) { 209 entry = kmalloc(sizeof *entry, GFP_KERNEL); 210 if (!entry) 211 goto out; 212 entry->qid = i; 213 list_add_tail(&entry->entry, &uctx->cqids); 214 } 215 } 216 out: 217 mutex_unlock(&uctx->lock); 218 PDBG("%s qid 0x%x\n", __func__, qid); 219 mutex_lock(&rdev->stats.lock); 220 if (rdev->stats.qid.cur > rdev->stats.qid.max) 221 rdev->stats.qid.max = rdev->stats.qid.cur; 222 mutex_unlock(&rdev->stats.lock); 223 return qid; 224 } 225 226 void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid, 227 struct c4iw_dev_ucontext *uctx) 228 { 229 struct c4iw_qid_list *entry; 230 231 entry = kmalloc(sizeof *entry, GFP_KERNEL); 232 if (!entry) 233 return; 234 PDBG("%s qid 0x%x\n", __func__, qid); 235 entry->qid = qid; 236 mutex_lock(&uctx->lock); 237 list_add_tail(&entry->entry, &uctx->qpids); 238 mutex_unlock(&uctx->lock); 239 } 240 241 void c4iw_destroy_resource(struct c4iw_resource *rscp) 242 { 243 c4iw_id_table_free(&rscp->tpt_table); 244 c4iw_id_table_free(&rscp->qid_table); 245 c4iw_id_table_free(&rscp->pdid_table); 246 } 247 248 /* 249 * PBL Memory Manager. Uses Linux generic allocator. 250 */ 251 252 #define MIN_PBL_SHIFT 8 /* 256B == min PBL size (32 entries) */ 253 254 u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size) 255 { 256 unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size); 257 PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size); 258 mutex_lock(&rdev->stats.lock); 259 if (addr) { 260 rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT); 261 if (rdev->stats.pbl.cur > rdev->stats.pbl.max) 262 rdev->stats.pbl.max = rdev->stats.pbl.cur; 263 } else 264 rdev->stats.pbl.fail++; 265 mutex_unlock(&rdev->stats.lock); 266 return (u32)addr; 267 } 268 269 void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size) 270 { 271 PDBG("%s addr 0x%x size %d\n", __func__, addr, size); 272 mutex_lock(&rdev->stats.lock); 273 rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT); 274 mutex_unlock(&rdev->stats.lock); 275 gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size); 276 } 277 278 int c4iw_pblpool_create(struct c4iw_rdev *rdev) 279 { 280 unsigned pbl_start, pbl_chunk, pbl_top; 281 282 rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1); 283 if (!rdev->pbl_pool) 284 return -ENOMEM; 285 286 pbl_start = rdev->lldi.vr->pbl.start; 287 pbl_chunk = rdev->lldi.vr->pbl.size; 288 pbl_top = pbl_start + pbl_chunk; 289 290 while (pbl_start < pbl_top) { 291 pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk); 292 if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) { 293 PDBG("%s failed to add PBL chunk (%x/%x)\n", 294 __func__, pbl_start, pbl_chunk); 295 if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) { 296 printk(KERN_WARNING MOD 297 "Failed to add all PBL chunks (%x/%x)\n", 298 pbl_start, 299 pbl_top - pbl_start); 300 return 0; 301 } 302 pbl_chunk >>= 1; 303 } else { 304 PDBG("%s added PBL chunk (%x/%x)\n", 305 __func__, pbl_start, pbl_chunk); 306 pbl_start += pbl_chunk; 307 } 308 } 309 310 return 0; 311 } 312 313 void c4iw_pblpool_destroy(struct c4iw_rdev *rdev) 314 { 315 gen_pool_destroy(rdev->pbl_pool); 316 } 317 318 /* 319 * RQT Memory Manager. Uses Linux generic allocator. 320 */ 321 322 #define MIN_RQT_SHIFT 10 /* 1KB == min RQT size (16 entries) */ 323 324 u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size) 325 { 326 unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6); 327 PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6); 328 if (!addr) 329 pr_warn_ratelimited(MOD "%s: Out of RQT memory\n", 330 pci_name(rdev->lldi.pdev)); 331 mutex_lock(&rdev->stats.lock); 332 if (addr) { 333 rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT); 334 if (rdev->stats.rqt.cur > rdev->stats.rqt.max) 335 rdev->stats.rqt.max = rdev->stats.rqt.cur; 336 } else 337 rdev->stats.rqt.fail++; 338 mutex_unlock(&rdev->stats.lock); 339 return (u32)addr; 340 } 341 342 void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size) 343 { 344 PDBG("%s addr 0x%x size %d\n", __func__, addr, size << 6); 345 mutex_lock(&rdev->stats.lock); 346 rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT); 347 mutex_unlock(&rdev->stats.lock); 348 gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6); 349 } 350 351 int c4iw_rqtpool_create(struct c4iw_rdev *rdev) 352 { 353 unsigned rqt_start, rqt_chunk, rqt_top; 354 355 rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1); 356 if (!rdev->rqt_pool) 357 return -ENOMEM; 358 359 rqt_start = rdev->lldi.vr->rq.start; 360 rqt_chunk = rdev->lldi.vr->rq.size; 361 rqt_top = rqt_start + rqt_chunk; 362 363 while (rqt_start < rqt_top) { 364 rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk); 365 if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) { 366 PDBG("%s failed to add RQT chunk (%x/%x)\n", 367 __func__, rqt_start, rqt_chunk); 368 if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) { 369 printk(KERN_WARNING MOD 370 "Failed to add all RQT chunks (%x/%x)\n", 371 rqt_start, rqt_top - rqt_start); 372 return 0; 373 } 374 rqt_chunk >>= 1; 375 } else { 376 PDBG("%s added RQT chunk (%x/%x)\n", 377 __func__, rqt_start, rqt_chunk); 378 rqt_start += rqt_chunk; 379 } 380 } 381 return 0; 382 } 383 384 void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev) 385 { 386 gen_pool_destroy(rdev->rqt_pool); 387 } 388 389 /* 390 * On-Chip QP Memory. 391 */ 392 #define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */ 393 394 u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size) 395 { 396 unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size); 397 PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size); 398 if (addr) { 399 mutex_lock(&rdev->stats.lock); 400 rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT); 401 if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max) 402 rdev->stats.ocqp.max = rdev->stats.ocqp.cur; 403 mutex_unlock(&rdev->stats.lock); 404 } 405 return (u32)addr; 406 } 407 408 void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size) 409 { 410 PDBG("%s addr 0x%x size %d\n", __func__, addr, size); 411 mutex_lock(&rdev->stats.lock); 412 rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT); 413 mutex_unlock(&rdev->stats.lock); 414 gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size); 415 } 416 417 int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev) 418 { 419 unsigned start, chunk, top; 420 421 rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1); 422 if (!rdev->ocqp_pool) 423 return -ENOMEM; 424 425 start = rdev->lldi.vr->ocq.start; 426 chunk = rdev->lldi.vr->ocq.size; 427 top = start + chunk; 428 429 while (start < top) { 430 chunk = min(top - start + 1, chunk); 431 if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) { 432 PDBG("%s failed to add OCQP chunk (%x/%x)\n", 433 __func__, start, chunk); 434 if (chunk <= 1024 << MIN_OCQP_SHIFT) { 435 printk(KERN_WARNING MOD 436 "Failed to add all OCQP chunks (%x/%x)\n", 437 start, top - start); 438 return 0; 439 } 440 chunk >>= 1; 441 } else { 442 PDBG("%s added OCQP chunk (%x/%x)\n", 443 __func__, start, chunk); 444 start += chunk; 445 } 446 } 447 return 0; 448 } 449 450 void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev) 451 { 452 gen_pool_destroy(rdev->ocqp_pool); 453 } 454