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        PDBG("%s entry 0x%x\n", __func__, 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        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
 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        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
 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        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
 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        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
 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        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
 269void 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
 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                        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
 313void 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
 324u32 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
 342void 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
 351int 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
 384void 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
 394u32 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
 408void 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
 417int 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
 450void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
 451{
 452        gen_pool_destroy(rdev->ocqp_pool);
 453}
 454