linux/drivers/infiniband/hw/cxgb4/resource.c
<<
>>
Prefs
   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/kernel.h>
  34#include <linux/random.h>
  35#include <linux/slab.h>
  36#include <linux/kfifo.h>
  37#include <linux/spinlock.h>
  38#include <linux/errno.h>
  39#include <linux/genalloc.h>
  40#include "iw_cxgb4.h"
  41
  42#define RANDOM_SIZE 16
  43
  44static int __c4iw_init_resource_fifo(struct kfifo *fifo,
  45                                   spinlock_t *fifo_lock,
  46                                   u32 nr, u32 skip_low,
  47                                   u32 skip_high,
  48                                   int random)
  49{
  50        u32 i, j, entry = 0, idx;
  51        u32 random_bytes;
  52        u32 rarray[16];
  53        spin_lock_init(fifo_lock);
  54
  55        if (kfifo_alloc(fifo, nr * sizeof(u32), GFP_KERNEL))
  56                return -ENOMEM;
  57
  58        for (i = 0; i < skip_low + skip_high; i++)
  59                kfifo_in(fifo, (unsigned char *) &entry, sizeof(u32));
  60        if (random) {
  61                j = 0;
  62                random_bytes = random32();
  63                for (i = 0; i < RANDOM_SIZE; i++)
  64                        rarray[i] = i + skip_low;
  65                for (i = skip_low + RANDOM_SIZE; i < nr - skip_high; i++) {
  66                        if (j >= RANDOM_SIZE) {
  67                                j = 0;
  68                                random_bytes = random32();
  69                        }
  70                        idx = (random_bytes >> (j * 2)) & 0xF;
  71                        kfifo_in(fifo,
  72                                (unsigned char *) &rarray[idx],
  73                                sizeof(u32));
  74                        rarray[idx] = i;
  75                        j++;
  76                }
  77                for (i = 0; i < RANDOM_SIZE; i++)
  78                        kfifo_in(fifo,
  79                                (unsigned char *) &rarray[i],
  80                                sizeof(u32));
  81        } else
  82                for (i = skip_low; i < nr - skip_high; i++)
  83                        kfifo_in(fifo, (unsigned char *) &i, sizeof(u32));
  84
  85        for (i = 0; i < skip_low + skip_high; i++)
  86                if (kfifo_out_locked(fifo, (unsigned char *) &entry,
  87                                     sizeof(u32), fifo_lock))
  88                        break;
  89        return 0;
  90}
  91
  92static int c4iw_init_resource_fifo(struct kfifo *fifo, spinlock_t * fifo_lock,
  93                                   u32 nr, u32 skip_low, u32 skip_high)
  94{
  95        return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low,
  96                                          skip_high, 0);
  97}
  98
  99static int c4iw_init_resource_fifo_random(struct kfifo *fifo,
 100                                   spinlock_t *fifo_lock,
 101                                   u32 nr, u32 skip_low, u32 skip_high)
 102{
 103        return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low,
 104                                          skip_high, 1);
 105}
 106
 107static int c4iw_init_qid_fifo(struct c4iw_rdev *rdev)
 108{
 109        u32 i;
 110
 111        spin_lock_init(&rdev->resource.qid_fifo_lock);
 112
 113        if (kfifo_alloc(&rdev->resource.qid_fifo, rdev->lldi.vr->qp.size *
 114                        sizeof(u32), GFP_KERNEL))
 115                return -ENOMEM;
 116
 117        for (i = rdev->lldi.vr->qp.start;
 118             i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)
 119                if (!(i & rdev->qpmask))
 120                        kfifo_in(&rdev->resource.qid_fifo,
 121                                    (unsigned char *) &i, sizeof(u32));
 122        return 0;
 123}
 124
 125/* nr_* must be power of 2 */
 126int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid)
 127{
 128        int err = 0;
 129        err = c4iw_init_resource_fifo_random(&rdev->resource.tpt_fifo,
 130                                             &rdev->resource.tpt_fifo_lock,
 131                                             nr_tpt, 1, 0);
 132        if (err)
 133                goto tpt_err;
 134        err = c4iw_init_qid_fifo(rdev);
 135        if (err)
 136                goto qid_err;
 137        err = c4iw_init_resource_fifo(&rdev->resource.pdid_fifo,
 138                                      &rdev->resource.pdid_fifo_lock,
 139                                      nr_pdid, 1, 0);
 140        if (err)
 141                goto pdid_err;
 142        return 0;
 143pdid_err:
 144        kfifo_free(&rdev->resource.qid_fifo);
 145qid_err:
 146        kfifo_free(&rdev->resource.tpt_fifo);
 147tpt_err:
 148        return -ENOMEM;
 149}
 150
 151/*
 152 * returns 0 if no resource available
 153 */
 154u32 c4iw_get_resource(struct kfifo *fifo, spinlock_t *lock)
 155{
 156        u32 entry;
 157        if (kfifo_out_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock))
 158                return entry;
 159        else
 160                return 0;
 161}
 162
 163void c4iw_put_resource(struct kfifo *fifo, u32 entry, spinlock_t *lock)
 164{
 165        PDBG("%s entry 0x%x\n", __func__, entry);
 166        kfifo_in_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock);
 167}
 168
 169u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
 170{
 171        struct c4iw_qid_list *entry;
 172        u32 qid;
 173        int i;
 174
 175        mutex_lock(&uctx->lock);
 176        if (!list_empty(&uctx->cqids)) {
 177                entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,
 178                                   entry);
 179                list_del(&entry->entry);
 180                qid = entry->qid;
 181                kfree(entry);
 182        } else {
 183                qid = c4iw_get_resource(&rdev->resource.qid_fifo,
 184                                        &rdev->resource.qid_fifo_lock);
 185                if (!qid)
 186                        goto out;
 187                for (i = qid+1; i & rdev->qpmask; i++) {
 188                        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 189                        if (!entry)
 190                                goto out;
 191                        entry->qid = i;
 192                        list_add_tail(&entry->entry, &uctx->cqids);
 193                }
 194
 195                /*
 196                 * now put the same ids on the qp list since they all
 197                 * map to the same db/gts page.
 198                 */
 199                entry = kmalloc(sizeof *entry, GFP_KERNEL);
 200                if (!entry)
 201                        goto out;
 202                entry->qid = qid;
 203                list_add_tail(&entry->entry, &uctx->qpids);
 204                for (i = qid+1; i & rdev->qpmask; i++) {
 205                        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 206                        if (!entry)
 207                                goto out;
 208                        entry->qid = i;
 209                        list_add_tail(&entry->entry, &uctx->qpids);
 210                }
 211        }
 212out:
 213        mutex_unlock(&uctx->lock);
 214        PDBG("%s qid 0x%x\n", __func__, qid);
 215        return qid;
 216}
 217
 218void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,
 219                   struct c4iw_dev_ucontext *uctx)
 220{
 221        struct c4iw_qid_list *entry;
 222
 223        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 224        if (!entry)
 225                return;
 226        PDBG("%s qid 0x%x\n", __func__, qid);
 227        entry->qid = qid;
 228        mutex_lock(&uctx->lock);
 229        list_add_tail(&entry->entry, &uctx->cqids);
 230        mutex_unlock(&uctx->lock);
 231}
 232
 233u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
 234{
 235        struct c4iw_qid_list *entry;
 236        u32 qid;
 237        int i;
 238
 239        mutex_lock(&uctx->lock);
 240        if (!list_empty(&uctx->qpids)) {
 241                entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,
 242                                   entry);
 243                list_del(&entry->entry);
 244                qid = entry->qid;
 245                kfree(entry);
 246        } else {
 247                qid = c4iw_get_resource(&rdev->resource.qid_fifo,
 248                                        &rdev->resource.qid_fifo_lock);
 249                if (!qid)
 250                        goto out;
 251                for (i = qid+1; i & rdev->qpmask; i++) {
 252                        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 253                        if (!entry)
 254                                goto out;
 255                        entry->qid = i;
 256                        list_add_tail(&entry->entry, &uctx->qpids);
 257                }
 258
 259                /*
 260                 * now put the same ids on the cq list since they all
 261                 * map to the same db/gts page.
 262                 */
 263                entry = kmalloc(sizeof *entry, GFP_KERNEL);
 264                if (!entry)
 265                        goto out;
 266                entry->qid = qid;
 267                list_add_tail(&entry->entry, &uctx->cqids);
 268                for (i = qid; i & rdev->qpmask; i++) {
 269                        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 270                        if (!entry)
 271                                goto out;
 272                        entry->qid = i;
 273                        list_add_tail(&entry->entry, &uctx->cqids);
 274                }
 275        }
 276out:
 277        mutex_unlock(&uctx->lock);
 278        PDBG("%s qid 0x%x\n", __func__, qid);
 279        return qid;
 280}
 281
 282void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,
 283                   struct c4iw_dev_ucontext *uctx)
 284{
 285        struct c4iw_qid_list *entry;
 286
 287        entry = kmalloc(sizeof *entry, GFP_KERNEL);
 288        if (!entry)
 289                return;
 290        PDBG("%s qid 0x%x\n", __func__, qid);
 291        entry->qid = qid;
 292        mutex_lock(&uctx->lock);
 293        list_add_tail(&entry->entry, &uctx->qpids);
 294        mutex_unlock(&uctx->lock);
 295}
 296
 297void c4iw_destroy_resource(struct c4iw_resource *rscp)
 298{
 299        kfifo_free(&rscp->tpt_fifo);
 300        kfifo_free(&rscp->qid_fifo);
 301        kfifo_free(&rscp->pdid_fifo);
 302}
 303
 304/*
 305 * PBL Memory Manager.  Uses Linux generic allocator.
 306 */
 307
 308#define MIN_PBL_SHIFT 8                 /* 256B == min PBL size (32 entries) */
 309
 310u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
 311{
 312        unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
 313        PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);
 314        if (!addr && printk_ratelimit())
 315                printk(KERN_WARNING MOD "%s: Out of PBL memory\n",
 316                       pci_name(rdev->lldi.pdev));
 317        return (u32)addr;
 318}
 319
 320void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 321{
 322        PDBG("%s addr 0x%x size %d\n", __func__, addr, size);
 323        gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);
 324}
 325
 326int c4iw_pblpool_create(struct c4iw_rdev *rdev)
 327{
 328        unsigned pbl_start, pbl_chunk, pbl_top;
 329
 330        rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1);
 331        if (!rdev->pbl_pool)
 332                return -ENOMEM;
 333
 334        pbl_start = rdev->lldi.vr->pbl.start;
 335        pbl_chunk = rdev->lldi.vr->pbl.size;
 336        pbl_top = pbl_start + pbl_chunk;
 337
 338        while (pbl_start < pbl_top) {
 339                pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk);
 340                if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) {
 341                        PDBG("%s failed to add PBL chunk (%x/%x)\n",
 342                             __func__, pbl_start, pbl_chunk);
 343                        if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {
 344                                printk(KERN_WARNING MOD
 345                                       "Failed to add all PBL chunks (%x/%x)\n",
 346                                       pbl_start,
 347                                       pbl_top - pbl_start);
 348                                return 0;
 349                        }
 350                        pbl_chunk >>= 1;
 351                } else {
 352                        PDBG("%s added PBL chunk (%x/%x)\n",
 353                             __func__, pbl_start, pbl_chunk);
 354                        pbl_start += pbl_chunk;
 355                }
 356        }
 357
 358        return 0;
 359}
 360
 361void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
 362{
 363        gen_pool_destroy(rdev->pbl_pool);
 364}
 365
 366/*
 367 * RQT Memory Manager.  Uses Linux generic allocator.
 368 */
 369
 370#define MIN_RQT_SHIFT 10        /* 1KB == min RQT size (16 entries) */
 371
 372u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
 373{
 374        unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
 375        PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6);
 376        if (!addr && printk_ratelimit())
 377                printk(KERN_WARNING MOD "%s: Out of RQT memory\n",
 378                       pci_name(rdev->lldi.pdev));
 379        return (u32)addr;
 380}
 381
 382void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 383{
 384        PDBG("%s addr 0x%x size %d\n", __func__, addr, size << 6);
 385        gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
 386}
 387
 388int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
 389{
 390        unsigned rqt_start, rqt_chunk, rqt_top;
 391
 392        rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);
 393        if (!rdev->rqt_pool)
 394                return -ENOMEM;
 395
 396        rqt_start = rdev->lldi.vr->rq.start;
 397        rqt_chunk = rdev->lldi.vr->rq.size;
 398        rqt_top = rqt_start + rqt_chunk;
 399
 400        while (rqt_start < rqt_top) {
 401                rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);
 402                if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {
 403                        PDBG("%s failed to add RQT chunk (%x/%x)\n",
 404                             __func__, rqt_start, rqt_chunk);
 405                        if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {
 406                                printk(KERN_WARNING MOD
 407                                       "Failed to add all RQT chunks (%x/%x)\n",
 408                                       rqt_start, rqt_top - rqt_start);
 409                                return 0;
 410                        }
 411                        rqt_chunk >>= 1;
 412                } else {
 413                        PDBG("%s added RQT chunk (%x/%x)\n",
 414                             __func__, rqt_start, rqt_chunk);
 415                        rqt_start += rqt_chunk;
 416                }
 417        }
 418        return 0;
 419}
 420
 421void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
 422{
 423        gen_pool_destroy(rdev->rqt_pool);
 424}
 425
 426/*
 427 * On-Chip QP Memory.
 428 */
 429#define MIN_OCQP_SHIFT 12       /* 4KB == min ocqp size */
 430
 431u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
 432{
 433        unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
 434        PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);
 435        return (u32)addr;
 436}
 437
 438void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 439{
 440        PDBG("%s addr 0x%x size %d\n", __func__, addr, size);
 441        gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
 442}
 443
 444int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
 445{
 446        unsigned start, chunk, top;
 447
 448        rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
 449        if (!rdev->ocqp_pool)
 450                return -ENOMEM;
 451
 452        start = rdev->lldi.vr->ocq.start;
 453        chunk = rdev->lldi.vr->ocq.size;
 454        top = start + chunk;
 455
 456        while (start < top) {
 457                chunk = min(top - start + 1, chunk);
 458                if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
 459                        PDBG("%s failed to add OCQP chunk (%x/%x)\n",
 460                             __func__, start, chunk);
 461                        if (chunk <= 1024 << MIN_OCQP_SHIFT) {
 462                                printk(KERN_WARNING MOD
 463                                       "Failed to add all OCQP chunks (%x/%x)\n",
 464                                       start, top - start);
 465                                return 0;
 466                        }
 467                        chunk >>= 1;
 468                } else {
 469                        PDBG("%s added OCQP chunk (%x/%x)\n",
 470                             __func__, start, chunk);
 471                        start += chunk;
 472                }
 473        }
 474        return 0;
 475}
 476
 477void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
 478{
 479        gen_pool_destroy(rdev->ocqp_pool);
 480}
 481