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/spinlock.h>
  34#include <linux/genalloc.h>
  35#include <linux/ratelimit.h>
  36#include "iw_cxgb4.h"
  37
  38static 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 */
  56int 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 */
  82u32 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
  91void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry)
  92{
  93        pr_debug("entry 0x%x\n", entry);
  94        c4iw_id_free(id_table, entry);
  95}
  96
  97u32 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        }
 142out:
 143        mutex_unlock(&uctx->lock);
 144        pr_debug("qid 0x%x\n", 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
 152void 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        pr_debug("qid 0x%x\n", 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
 167u32 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        }
 216out:
 217        mutex_unlock(&uctx->lock);
 218        pr_debug("qid 0x%x\n", 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
 226void 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        pr_debug("qid 0x%x\n", 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
 241void 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
 254u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
 255{
 256        unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
 257        pr_debug("addr 0x%x size %d\n", (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
 269void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 270{
 271        pr_debug("addr 0x%x size %d\n", 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
 278int 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                        pr_debug("failed to add PBL chunk (%x/%x)\n",
 294                                 pbl_start, pbl_chunk);
 295                        if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {
 296                                pr_warn("Failed to add all PBL chunks (%x/%x)\n",
 297                                        pbl_start, pbl_top - pbl_start);
 298                                return 0;
 299                        }
 300                        pbl_chunk >>= 1;
 301                } else {
 302                        pr_debug("added PBL chunk (%x/%x)\n",
 303                                 pbl_start, pbl_chunk);
 304                        pbl_start += pbl_chunk;
 305                }
 306        }
 307
 308        return 0;
 309}
 310
 311void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
 312{
 313        gen_pool_destroy(rdev->pbl_pool);
 314}
 315
 316/*
 317 * RQT Memory Manager.  Uses Linux generic allocator.
 318 */
 319
 320#define MIN_RQT_SHIFT 10        /* 1KB == min RQT size (16 entries) */
 321
 322u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
 323{
 324        unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
 325        pr_debug("addr 0x%x size %d\n", (u32)addr, size << 6);
 326        if (!addr)
 327                pr_warn_ratelimited("%s: Out of RQT memory\n",
 328                                    pci_name(rdev->lldi.pdev));
 329        mutex_lock(&rdev->stats.lock);
 330        if (addr) {
 331                rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
 332                if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
 333                        rdev->stats.rqt.max = rdev->stats.rqt.cur;
 334        } else
 335                rdev->stats.rqt.fail++;
 336        mutex_unlock(&rdev->stats.lock);
 337        return (u32)addr;
 338}
 339
 340void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 341{
 342        pr_debug("addr 0x%x size %d\n", addr, size << 6);
 343        mutex_lock(&rdev->stats.lock);
 344        rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
 345        mutex_unlock(&rdev->stats.lock);
 346        gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
 347}
 348
 349int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
 350{
 351        unsigned rqt_start, rqt_chunk, rqt_top;
 352
 353        rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);
 354        if (!rdev->rqt_pool)
 355                return -ENOMEM;
 356
 357        rqt_start = rdev->lldi.vr->rq.start;
 358        rqt_chunk = rdev->lldi.vr->rq.size;
 359        rqt_top = rqt_start + rqt_chunk;
 360
 361        while (rqt_start < rqt_top) {
 362                rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);
 363                if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {
 364                        pr_debug("failed to add RQT chunk (%x/%x)\n",
 365                                 rqt_start, rqt_chunk);
 366                        if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {
 367                                pr_warn("Failed to add all RQT chunks (%x/%x)\n",
 368                                        rqt_start, rqt_top - rqt_start);
 369                                return 0;
 370                        }
 371                        rqt_chunk >>= 1;
 372                } else {
 373                        pr_debug("added RQT chunk (%x/%x)\n",
 374                                 rqt_start, rqt_chunk);
 375                        rqt_start += rqt_chunk;
 376                }
 377        }
 378        return 0;
 379}
 380
 381void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
 382{
 383        gen_pool_destroy(rdev->rqt_pool);
 384}
 385
 386/*
 387 * On-Chip QP Memory.
 388 */
 389#define MIN_OCQP_SHIFT 12       /* 4KB == min ocqp size */
 390
 391u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
 392{
 393        unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
 394        pr_debug("addr 0x%x size %d\n", (u32)addr, size);
 395        if (addr) {
 396                mutex_lock(&rdev->stats.lock);
 397                rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT);
 398                if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max)
 399                        rdev->stats.ocqp.max = rdev->stats.ocqp.cur;
 400                mutex_unlock(&rdev->stats.lock);
 401        }
 402        return (u32)addr;
 403}
 404
 405void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
 406{
 407        pr_debug("addr 0x%x size %d\n", addr, size);
 408        mutex_lock(&rdev->stats.lock);
 409        rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT);
 410        mutex_unlock(&rdev->stats.lock);
 411        gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
 412}
 413
 414int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
 415{
 416        unsigned start, chunk, top;
 417
 418        rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
 419        if (!rdev->ocqp_pool)
 420                return -ENOMEM;
 421
 422        start = rdev->lldi.vr->ocq.start;
 423        chunk = rdev->lldi.vr->ocq.size;
 424        top = start + chunk;
 425
 426        while (start < top) {
 427                chunk = min(top - start + 1, chunk);
 428                if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
 429                        pr_debug("failed to add OCQP chunk (%x/%x)\n",
 430                                 start, chunk);
 431                        if (chunk <= 1024 << MIN_OCQP_SHIFT) {
 432                                pr_warn("Failed to add all OCQP chunks (%x/%x)\n",
 433                                        start, top - start);
 434                                return 0;
 435                        }
 436                        chunk >>= 1;
 437                } else {
 438                        pr_debug("added OCQP chunk (%x/%x)\n",
 439                                 start, chunk);
 440                        start += chunk;
 441                }
 442        }
 443        return 0;
 444}
 445
 446void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
 447{
 448        gen_pool_destroy(rdev->ocqp_pool);
 449}
 450