Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved.
0003  *
0004  * This software is available to you under a choice of one of two
0005  * licenses.  You may choose to be licensed under the terms of the GNU
0006  * General Public License (GPL) Version 2, available from the file
0007  * COPYING in the main directory of this source tree, or the
0008  * OpenIB.org BSD license below:
0009  *
0010  *     Redistribution and use in source and binary forms, with or
0011  *     without modification, are permitted provided that the following
0012  *     conditions are met:
0013  *
0014  *      - Redistributions of source code must retain the above
0015  *        copyright notice, this list of conditions and the following
0016  *        disclaimer.
0017  *
0018  *      - Redistributions in binary form must reproduce the above
0019  *        copyright notice, this list of conditions and the following
0020  *        disclaimer in the documentation and/or other materials
0021  *        provided with the distribution.
0022  *
0023  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0024  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0025  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0026  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
0027  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
0028  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0029  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0030  * SOFTWARE.
0031  */
0032 /* Crude resource management */
0033 #include <linux/spinlock.h>
0034 #include <linux/genalloc.h>
0035 #include <linux/ratelimit.h>
0036 #include "iw_cxgb4.h"
0037 
0038 static int c4iw_init_qid_table(struct c4iw_rdev *rdev)
0039 {
0040     u32 i;
0041 
0042     if (c4iw_id_table_alloc(&rdev->resource.qid_table,
0043                 rdev->lldi.vr->qp.start,
0044                 rdev->lldi.vr->qp.size,
0045                 rdev->lldi.vr->qp.size, 0))
0046         return -ENOMEM;
0047 
0048     for (i = rdev->lldi.vr->qp.start;
0049         i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)
0050         if (!(i & rdev->qpmask))
0051             c4iw_id_free(&rdev->resource.qid_table, i);
0052     return 0;
0053 }
0054 
0055 /* nr_* must be power of 2 */
0056 int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt,
0057                u32 nr_pdid, u32 nr_srqt)
0058 {
0059     int err = 0;
0060     err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1,
0061                     C4IW_ID_TABLE_F_RANDOM);
0062     if (err)
0063         goto tpt_err;
0064     err = c4iw_init_qid_table(rdev);
0065     if (err)
0066         goto qid_err;
0067     err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0,
0068                     nr_pdid, 1, 0);
0069     if (err)
0070         goto pdid_err;
0071     if (!nr_srqt)
0072         err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
0073                       1, 1, 0);
0074     else
0075         err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
0076                       nr_srqt, 0, 0);
0077     if (err)
0078         goto srq_err;
0079     return 0;
0080  srq_err:
0081     c4iw_id_table_free(&rdev->resource.pdid_table);
0082  pdid_err:
0083     c4iw_id_table_free(&rdev->resource.qid_table);
0084  qid_err:
0085     c4iw_id_table_free(&rdev->resource.tpt_table);
0086  tpt_err:
0087     return -ENOMEM;
0088 }
0089 
0090 /*
0091  * returns 0 if no resource available
0092  */
0093 u32 c4iw_get_resource(struct c4iw_id_table *id_table)
0094 {
0095     u32 entry;
0096     entry = c4iw_id_alloc(id_table);
0097     if (entry == (u32)(-1))
0098         return 0;
0099     return entry;
0100 }
0101 
0102 void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry)
0103 {
0104     pr_debug("entry 0x%x\n", entry);
0105     c4iw_id_free(id_table, entry);
0106 }
0107 
0108 u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
0109 {
0110     struct c4iw_qid_list *entry;
0111     u32 qid;
0112     int i;
0113 
0114     mutex_lock(&uctx->lock);
0115     if (!list_empty(&uctx->cqids)) {
0116         entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,
0117                    entry);
0118         list_del(&entry->entry);
0119         qid = entry->qid;
0120         kfree(entry);
0121     } else {
0122         qid = c4iw_get_resource(&rdev->resource.qid_table);
0123         if (!qid)
0124             goto out;
0125         mutex_lock(&rdev->stats.lock);
0126         rdev->stats.qid.cur += rdev->qpmask + 1;
0127         mutex_unlock(&rdev->stats.lock);
0128         for (i = qid+1; i & rdev->qpmask; i++) {
0129             entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0130             if (!entry)
0131                 goto out;
0132             entry->qid = i;
0133             list_add_tail(&entry->entry, &uctx->cqids);
0134         }
0135 
0136         /*
0137          * now put the same ids on the qp list since they all
0138          * map to the same db/gts page.
0139          */
0140         entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0141         if (!entry)
0142             goto out;
0143         entry->qid = qid;
0144         list_add_tail(&entry->entry, &uctx->qpids);
0145         for (i = qid+1; i & rdev->qpmask; i++) {
0146             entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0147             if (!entry)
0148                 goto out;
0149             entry->qid = i;
0150             list_add_tail(&entry->entry, &uctx->qpids);
0151         }
0152     }
0153 out:
0154     mutex_unlock(&uctx->lock);
0155     pr_debug("qid 0x%x\n", qid);
0156     mutex_lock(&rdev->stats.lock);
0157     if (rdev->stats.qid.cur > rdev->stats.qid.max)
0158         rdev->stats.qid.max = rdev->stats.qid.cur;
0159     mutex_unlock(&rdev->stats.lock);
0160     return qid;
0161 }
0162 
0163 void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,
0164            struct c4iw_dev_ucontext *uctx)
0165 {
0166     struct c4iw_qid_list *entry;
0167 
0168     entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0169     if (!entry)
0170         return;
0171     pr_debug("qid 0x%x\n", qid);
0172     entry->qid = qid;
0173     mutex_lock(&uctx->lock);
0174     list_add_tail(&entry->entry, &uctx->cqids);
0175     mutex_unlock(&uctx->lock);
0176 }
0177 
0178 u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
0179 {
0180     struct c4iw_qid_list *entry;
0181     u32 qid;
0182     int i;
0183 
0184     mutex_lock(&uctx->lock);
0185     if (!list_empty(&uctx->qpids)) {
0186         entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,
0187                    entry);
0188         list_del(&entry->entry);
0189         qid = entry->qid;
0190         kfree(entry);
0191     } else {
0192         qid = c4iw_get_resource(&rdev->resource.qid_table);
0193         if (!qid) {
0194             mutex_lock(&rdev->stats.lock);
0195             rdev->stats.qid.fail++;
0196             mutex_unlock(&rdev->stats.lock);
0197             goto out;
0198         }
0199         mutex_lock(&rdev->stats.lock);
0200         rdev->stats.qid.cur += rdev->qpmask + 1;
0201         mutex_unlock(&rdev->stats.lock);
0202         for (i = qid+1; i & rdev->qpmask; i++) {
0203             entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0204             if (!entry)
0205                 goto out;
0206             entry->qid = i;
0207             list_add_tail(&entry->entry, &uctx->qpids);
0208         }
0209 
0210         /*
0211          * now put the same ids on the cq list since they all
0212          * map to the same db/gts page.
0213          */
0214         entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0215         if (!entry)
0216             goto out;
0217         entry->qid = qid;
0218         list_add_tail(&entry->entry, &uctx->cqids);
0219         for (i = qid + 1; i & rdev->qpmask; i++) {
0220             entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0221             if (!entry)
0222                 goto out;
0223             entry->qid = i;
0224             list_add_tail(&entry->entry, &uctx->cqids);
0225         }
0226     }
0227 out:
0228     mutex_unlock(&uctx->lock);
0229     pr_debug("qid 0x%x\n", qid);
0230     mutex_lock(&rdev->stats.lock);
0231     if (rdev->stats.qid.cur > rdev->stats.qid.max)
0232         rdev->stats.qid.max = rdev->stats.qid.cur;
0233     mutex_unlock(&rdev->stats.lock);
0234     return qid;
0235 }
0236 
0237 void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,
0238            struct c4iw_dev_ucontext *uctx)
0239 {
0240     struct c4iw_qid_list *entry;
0241 
0242     entry = kmalloc(sizeof(*entry), GFP_KERNEL);
0243     if (!entry)
0244         return;
0245     pr_debug("qid 0x%x\n", qid);
0246     entry->qid = qid;
0247     mutex_lock(&uctx->lock);
0248     list_add_tail(&entry->entry, &uctx->qpids);
0249     mutex_unlock(&uctx->lock);
0250 }
0251 
0252 void c4iw_destroy_resource(struct c4iw_resource *rscp)
0253 {
0254     c4iw_id_table_free(&rscp->tpt_table);
0255     c4iw_id_table_free(&rscp->qid_table);
0256     c4iw_id_table_free(&rscp->pdid_table);
0257 }
0258 
0259 /*
0260  * PBL Memory Manager.  Uses Linux generic allocator.
0261  */
0262 
0263 #define MIN_PBL_SHIFT 8         /* 256B == min PBL size (32 entries) */
0264 
0265 u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
0266 {
0267     unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
0268     pr_debug("addr 0x%x size %d\n", (u32)addr, size);
0269     mutex_lock(&rdev->stats.lock);
0270     if (addr) {
0271         rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT);
0272         if (rdev->stats.pbl.cur > rdev->stats.pbl.max)
0273             rdev->stats.pbl.max = rdev->stats.pbl.cur;
0274         kref_get(&rdev->pbl_kref);
0275     } else
0276         rdev->stats.pbl.fail++;
0277     mutex_unlock(&rdev->stats.lock);
0278     return (u32)addr;
0279 }
0280 
0281 static void destroy_pblpool(struct kref *kref)
0282 {
0283     struct c4iw_rdev *rdev;
0284 
0285     rdev = container_of(kref, struct c4iw_rdev, pbl_kref);
0286     gen_pool_destroy(rdev->pbl_pool);
0287     complete(&rdev->pbl_compl);
0288 }
0289 
0290 void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
0291 {
0292     pr_debug("addr 0x%x size %d\n", addr, size);
0293     mutex_lock(&rdev->stats.lock);
0294     rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT);
0295     mutex_unlock(&rdev->stats.lock);
0296     gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);
0297     kref_put(&rdev->pbl_kref, destroy_pblpool);
0298 }
0299 
0300 int c4iw_pblpool_create(struct c4iw_rdev *rdev)
0301 {
0302     unsigned pbl_start, pbl_chunk, pbl_top;
0303 
0304     rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1);
0305     if (!rdev->pbl_pool)
0306         return -ENOMEM;
0307 
0308     pbl_start = rdev->lldi.vr->pbl.start;
0309     pbl_chunk = rdev->lldi.vr->pbl.size;
0310     pbl_top = pbl_start + pbl_chunk;
0311 
0312     while (pbl_start < pbl_top) {
0313         pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk);
0314         if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) {
0315             pr_debug("failed to add PBL chunk (%x/%x)\n",
0316                  pbl_start, pbl_chunk);
0317             if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {
0318                 pr_warn("Failed to add all PBL chunks (%x/%x)\n",
0319                     pbl_start, pbl_top - pbl_start);
0320                 return 0;
0321             }
0322             pbl_chunk >>= 1;
0323         } else {
0324             pr_debug("added PBL chunk (%x/%x)\n",
0325                  pbl_start, pbl_chunk);
0326             pbl_start += pbl_chunk;
0327         }
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
0334 {
0335     kref_put(&rdev->pbl_kref, destroy_pblpool);
0336 }
0337 
0338 /*
0339  * RQT Memory Manager.  Uses Linux generic allocator.
0340  */
0341 
0342 #define MIN_RQT_SHIFT 10    /* 1KB == min RQT size (16 entries) */
0343 
0344 u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
0345 {
0346     unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
0347     pr_debug("addr 0x%x size %d\n", (u32)addr, size << 6);
0348     if (!addr)
0349         pr_warn_ratelimited("%s: Out of RQT memory\n",
0350                     pci_name(rdev->lldi.pdev));
0351     mutex_lock(&rdev->stats.lock);
0352     if (addr) {
0353         rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
0354         if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
0355             rdev->stats.rqt.max = rdev->stats.rqt.cur;
0356         kref_get(&rdev->rqt_kref);
0357     } else
0358         rdev->stats.rqt.fail++;
0359     mutex_unlock(&rdev->stats.lock);
0360     return (u32)addr;
0361 }
0362 
0363 static void destroy_rqtpool(struct kref *kref)
0364 {
0365     struct c4iw_rdev *rdev;
0366 
0367     rdev = container_of(kref, struct c4iw_rdev, rqt_kref);
0368     gen_pool_destroy(rdev->rqt_pool);
0369     complete(&rdev->rqt_compl);
0370 }
0371 
0372 void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
0373 {
0374     pr_debug("addr 0x%x size %d\n", addr, size << 6);
0375     mutex_lock(&rdev->stats.lock);
0376     rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
0377     mutex_unlock(&rdev->stats.lock);
0378     gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
0379     kref_put(&rdev->rqt_kref, destroy_rqtpool);
0380 }
0381 
0382 int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
0383 {
0384     unsigned rqt_start, rqt_chunk, rqt_top;
0385     int skip = 0;
0386 
0387     rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);
0388     if (!rdev->rqt_pool)
0389         return -ENOMEM;
0390 
0391     /*
0392      * If SRQs are supported, then never use the first RQE from
0393      * the RQT region. This is because HW uses RQT index 0 as NULL.
0394      */
0395     if (rdev->lldi.vr->srq.size)
0396         skip = T4_RQT_ENTRY_SIZE;
0397 
0398     rqt_start = rdev->lldi.vr->rq.start + skip;
0399     rqt_chunk = rdev->lldi.vr->rq.size - skip;
0400     rqt_top = rqt_start + rqt_chunk;
0401 
0402     while (rqt_start < rqt_top) {
0403         rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);
0404         if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {
0405             pr_debug("failed to add RQT chunk (%x/%x)\n",
0406                  rqt_start, rqt_chunk);
0407             if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {
0408                 pr_warn("Failed to add all RQT chunks (%x/%x)\n",
0409                     rqt_start, rqt_top - rqt_start);
0410                 return 0;
0411             }
0412             rqt_chunk >>= 1;
0413         } else {
0414             pr_debug("added RQT chunk (%x/%x)\n",
0415                  rqt_start, rqt_chunk);
0416             rqt_start += rqt_chunk;
0417         }
0418     }
0419     return 0;
0420 }
0421 
0422 void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
0423 {
0424     kref_put(&rdev->rqt_kref, destroy_rqtpool);
0425 }
0426 
0427 int c4iw_alloc_srq_idx(struct c4iw_rdev *rdev)
0428 {
0429     int idx;
0430 
0431     idx = c4iw_id_alloc(&rdev->resource.srq_table);
0432     mutex_lock(&rdev->stats.lock);
0433     if (idx == -1) {
0434         rdev->stats.srqt.fail++;
0435         mutex_unlock(&rdev->stats.lock);
0436         return -ENOMEM;
0437     }
0438     rdev->stats.srqt.cur++;
0439     if (rdev->stats.srqt.cur > rdev->stats.srqt.max)
0440         rdev->stats.srqt.max = rdev->stats.srqt.cur;
0441     mutex_unlock(&rdev->stats.lock);
0442     return idx;
0443 }
0444 
0445 void c4iw_free_srq_idx(struct c4iw_rdev *rdev, int idx)
0446 {
0447     c4iw_id_free(&rdev->resource.srq_table, idx);
0448     mutex_lock(&rdev->stats.lock);
0449     rdev->stats.srqt.cur--;
0450     mutex_unlock(&rdev->stats.lock);
0451 }
0452 
0453 /*
0454  * On-Chip QP Memory.
0455  */
0456 #define MIN_OCQP_SHIFT 12   /* 4KB == min ocqp size */
0457 
0458 u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
0459 {
0460     unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
0461     pr_debug("addr 0x%x size %d\n", (u32)addr, size);
0462     if (addr) {
0463         mutex_lock(&rdev->stats.lock);
0464         rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT);
0465         if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max)
0466             rdev->stats.ocqp.max = rdev->stats.ocqp.cur;
0467         mutex_unlock(&rdev->stats.lock);
0468     }
0469     return (u32)addr;
0470 }
0471 
0472 void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
0473 {
0474     pr_debug("addr 0x%x size %d\n", addr, size);
0475     mutex_lock(&rdev->stats.lock);
0476     rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT);
0477     mutex_unlock(&rdev->stats.lock);
0478     gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
0479 }
0480 
0481 int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
0482 {
0483     unsigned start, chunk, top;
0484 
0485     rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
0486     if (!rdev->ocqp_pool)
0487         return -ENOMEM;
0488 
0489     start = rdev->lldi.vr->ocq.start;
0490     chunk = rdev->lldi.vr->ocq.size;
0491     top = start + chunk;
0492 
0493     while (start < top) {
0494         chunk = min(top - start + 1, chunk);
0495         if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
0496             pr_debug("failed to add OCQP chunk (%x/%x)\n",
0497                  start, chunk);
0498             if (chunk <= 1024 << MIN_OCQP_SHIFT) {
0499                 pr_warn("Failed to add all OCQP chunks (%x/%x)\n",
0500                     start, top - start);
0501                 return 0;
0502             }
0503             chunk >>= 1;
0504         } else {
0505             pr_debug("added OCQP chunk (%x/%x)\n",
0506                  start, chunk);
0507             start += chunk;
0508         }
0509     }
0510     return 0;
0511 }
0512 
0513 void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
0514 {
0515     gen_pool_destroy(rdev->ocqp_pool);
0516 }