162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci/* Crude resource management */ 3362306a36Sopenharmony_ci#include <linux/spinlock.h> 3462306a36Sopenharmony_ci#include <linux/genalloc.h> 3562306a36Sopenharmony_ci#include <linux/ratelimit.h> 3662306a36Sopenharmony_ci#include "iw_cxgb4.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int c4iw_init_qid_table(struct c4iw_rdev *rdev) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u32 i; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (c4iw_id_table_alloc(&rdev->resource.qid_table, 4362306a36Sopenharmony_ci rdev->lldi.vr->qp.start, 4462306a36Sopenharmony_ci rdev->lldi.vr->qp.size, 4562306a36Sopenharmony_ci rdev->lldi.vr->qp.size, 0)) 4662306a36Sopenharmony_ci return -ENOMEM; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = rdev->lldi.vr->qp.start; 4962306a36Sopenharmony_ci i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++) 5062306a36Sopenharmony_ci if (!(i & rdev->qpmask)) 5162306a36Sopenharmony_ci c4iw_id_free(&rdev->resource.qid_table, i); 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* nr_* must be power of 2 */ 5662306a36Sopenharmony_ciint c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, 5762306a36Sopenharmony_ci u32 nr_pdid, u32 nr_srqt) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int err = 0; 6062306a36Sopenharmony_ci err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1, 6162306a36Sopenharmony_ci C4IW_ID_TABLE_F_RANDOM); 6262306a36Sopenharmony_ci if (err) 6362306a36Sopenharmony_ci goto tpt_err; 6462306a36Sopenharmony_ci err = c4iw_init_qid_table(rdev); 6562306a36Sopenharmony_ci if (err) 6662306a36Sopenharmony_ci goto qid_err; 6762306a36Sopenharmony_ci err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0, 6862306a36Sopenharmony_ci nr_pdid, 1, 0); 6962306a36Sopenharmony_ci if (err) 7062306a36Sopenharmony_ci goto pdid_err; 7162306a36Sopenharmony_ci if (!nr_srqt) 7262306a36Sopenharmony_ci err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0, 7362306a36Sopenharmony_ci 1, 1, 0); 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0, 7662306a36Sopenharmony_ci nr_srqt, 0, 0); 7762306a36Sopenharmony_ci if (err) 7862306a36Sopenharmony_ci goto srq_err; 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci srq_err: 8162306a36Sopenharmony_ci c4iw_id_table_free(&rdev->resource.pdid_table); 8262306a36Sopenharmony_ci pdid_err: 8362306a36Sopenharmony_ci c4iw_id_table_free(&rdev->resource.qid_table); 8462306a36Sopenharmony_ci qid_err: 8562306a36Sopenharmony_ci c4iw_id_table_free(&rdev->resource.tpt_table); 8662306a36Sopenharmony_ci tpt_err: 8762306a36Sopenharmony_ci return -ENOMEM; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * returns 0 if no resource available 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ciu32 c4iw_get_resource(struct c4iw_id_table *id_table) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u32 entry; 9662306a36Sopenharmony_ci entry = c4iw_id_alloc(id_table); 9762306a36Sopenharmony_ci if (entry == (u32)(-1)) 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci return entry; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci pr_debug("entry 0x%x\n", entry); 10562306a36Sopenharmony_ci c4iw_id_free(id_table, entry); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciu32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct c4iw_qid_list *entry; 11162306a36Sopenharmony_ci u32 qid; 11262306a36Sopenharmony_ci int i; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci mutex_lock(&uctx->lock); 11562306a36Sopenharmony_ci if (!list_empty(&uctx->cqids)) { 11662306a36Sopenharmony_ci entry = list_entry(uctx->cqids.next, struct c4iw_qid_list, 11762306a36Sopenharmony_ci entry); 11862306a36Sopenharmony_ci list_del(&entry->entry); 11962306a36Sopenharmony_ci qid = entry->qid; 12062306a36Sopenharmony_ci kfree(entry); 12162306a36Sopenharmony_ci } else { 12262306a36Sopenharmony_ci qid = c4iw_get_resource(&rdev->resource.qid_table); 12362306a36Sopenharmony_ci if (!qid) 12462306a36Sopenharmony_ci goto out; 12562306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 12662306a36Sopenharmony_ci rdev->stats.qid.cur += rdev->qpmask + 1; 12762306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 12862306a36Sopenharmony_ci for (i = qid+1; i & rdev->qpmask; i++) { 12962306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!entry) 13162306a36Sopenharmony_ci goto out; 13262306a36Sopenharmony_ci entry->qid = i; 13362306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->cqids); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * now put the same ids on the qp list since they all 13862306a36Sopenharmony_ci * map to the same db/gts page. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 14162306a36Sopenharmony_ci if (!entry) 14262306a36Sopenharmony_ci goto out; 14362306a36Sopenharmony_ci entry->qid = qid; 14462306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->qpids); 14562306a36Sopenharmony_ci for (i = qid+1; i & rdev->qpmask; i++) { 14662306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 14762306a36Sopenharmony_ci if (!entry) 14862306a36Sopenharmony_ci goto out; 14962306a36Sopenharmony_ci entry->qid = i; 15062306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->qpids); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ciout: 15462306a36Sopenharmony_ci mutex_unlock(&uctx->lock); 15562306a36Sopenharmony_ci pr_debug("qid 0x%x\n", qid); 15662306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 15762306a36Sopenharmony_ci if (rdev->stats.qid.cur > rdev->stats.qid.max) 15862306a36Sopenharmony_ci rdev->stats.qid.max = rdev->stats.qid.cur; 15962306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 16062306a36Sopenharmony_ci return qid; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid, 16462306a36Sopenharmony_ci struct c4iw_dev_ucontext *uctx) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct c4iw_qid_list *entry; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 16962306a36Sopenharmony_ci if (!entry) 17062306a36Sopenharmony_ci return; 17162306a36Sopenharmony_ci pr_debug("qid 0x%x\n", qid); 17262306a36Sopenharmony_ci entry->qid = qid; 17362306a36Sopenharmony_ci mutex_lock(&uctx->lock); 17462306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->cqids); 17562306a36Sopenharmony_ci mutex_unlock(&uctx->lock); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciu32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct c4iw_qid_list *entry; 18162306a36Sopenharmony_ci u32 qid; 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mutex_lock(&uctx->lock); 18562306a36Sopenharmony_ci if (!list_empty(&uctx->qpids)) { 18662306a36Sopenharmony_ci entry = list_entry(uctx->qpids.next, struct c4iw_qid_list, 18762306a36Sopenharmony_ci entry); 18862306a36Sopenharmony_ci list_del(&entry->entry); 18962306a36Sopenharmony_ci qid = entry->qid; 19062306a36Sopenharmony_ci kfree(entry); 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci qid = c4iw_get_resource(&rdev->resource.qid_table); 19362306a36Sopenharmony_ci if (!qid) { 19462306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 19562306a36Sopenharmony_ci rdev->stats.qid.fail++; 19662306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 19762306a36Sopenharmony_ci goto out; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 20062306a36Sopenharmony_ci rdev->stats.qid.cur += rdev->qpmask + 1; 20162306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 20262306a36Sopenharmony_ci for (i = qid+1; i & rdev->qpmask; i++) { 20362306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 20462306a36Sopenharmony_ci if (!entry) 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci entry->qid = i; 20762306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->qpids); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * now put the same ids on the cq list since they all 21262306a36Sopenharmony_ci * map to the same db/gts page. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 21562306a36Sopenharmony_ci if (!entry) 21662306a36Sopenharmony_ci goto out; 21762306a36Sopenharmony_ci entry->qid = qid; 21862306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->cqids); 21962306a36Sopenharmony_ci for (i = qid + 1; i & rdev->qpmask; i++) { 22062306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 22162306a36Sopenharmony_ci if (!entry) 22262306a36Sopenharmony_ci goto out; 22362306a36Sopenharmony_ci entry->qid = i; 22462306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->cqids); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ciout: 22862306a36Sopenharmony_ci mutex_unlock(&uctx->lock); 22962306a36Sopenharmony_ci pr_debug("qid 0x%x\n", qid); 23062306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 23162306a36Sopenharmony_ci if (rdev->stats.qid.cur > rdev->stats.qid.max) 23262306a36Sopenharmony_ci rdev->stats.qid.max = rdev->stats.qid.cur; 23362306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 23462306a36Sopenharmony_ci return qid; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_civoid c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid, 23862306a36Sopenharmony_ci struct c4iw_dev_ucontext *uctx) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct c4iw_qid_list *entry; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 24362306a36Sopenharmony_ci if (!entry) 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci pr_debug("qid 0x%x\n", qid); 24662306a36Sopenharmony_ci entry->qid = qid; 24762306a36Sopenharmony_ci mutex_lock(&uctx->lock); 24862306a36Sopenharmony_ci list_add_tail(&entry->entry, &uctx->qpids); 24962306a36Sopenharmony_ci mutex_unlock(&uctx->lock); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_civoid c4iw_destroy_resource(struct c4iw_resource *rscp) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci c4iw_id_table_free(&rscp->tpt_table); 25562306a36Sopenharmony_ci c4iw_id_table_free(&rscp->qid_table); 25662306a36Sopenharmony_ci c4iw_id_table_free(&rscp->pdid_table); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/* 26062306a36Sopenharmony_ci * PBL Memory Manager. Uses Linux generic allocator. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#define MIN_PBL_SHIFT 8 /* 256B == min PBL size (32 entries) */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciu32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size); 26862306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", (u32)addr, size); 26962306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 27062306a36Sopenharmony_ci if (addr) { 27162306a36Sopenharmony_ci rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT); 27262306a36Sopenharmony_ci if (rdev->stats.pbl.cur > rdev->stats.pbl.max) 27362306a36Sopenharmony_ci rdev->stats.pbl.max = rdev->stats.pbl.cur; 27462306a36Sopenharmony_ci kref_get(&rdev->pbl_kref); 27562306a36Sopenharmony_ci } else 27662306a36Sopenharmony_ci rdev->stats.pbl.fail++; 27762306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 27862306a36Sopenharmony_ci return (u32)addr; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void destroy_pblpool(struct kref *kref) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct c4iw_rdev *rdev; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci rdev = container_of(kref, struct c4iw_rdev, pbl_kref); 28662306a36Sopenharmony_ci gen_pool_destroy(rdev->pbl_pool); 28762306a36Sopenharmony_ci complete(&rdev->pbl_compl); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_civoid c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", addr, size); 29362306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 29462306a36Sopenharmony_ci rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT); 29562306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 29662306a36Sopenharmony_ci gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size); 29762306a36Sopenharmony_ci kref_put(&rdev->pbl_kref, destroy_pblpool); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint c4iw_pblpool_create(struct c4iw_rdev *rdev) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci unsigned pbl_start, pbl_chunk, pbl_top; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1); 30562306a36Sopenharmony_ci if (!rdev->pbl_pool) 30662306a36Sopenharmony_ci return -ENOMEM; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci pbl_start = rdev->lldi.vr->pbl.start; 30962306a36Sopenharmony_ci pbl_chunk = rdev->lldi.vr->pbl.size; 31062306a36Sopenharmony_ci pbl_top = pbl_start + pbl_chunk; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci while (pbl_start < pbl_top) { 31362306a36Sopenharmony_ci pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk); 31462306a36Sopenharmony_ci if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) { 31562306a36Sopenharmony_ci pr_debug("failed to add PBL chunk (%x/%x)\n", 31662306a36Sopenharmony_ci pbl_start, pbl_chunk); 31762306a36Sopenharmony_ci if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) { 31862306a36Sopenharmony_ci pr_warn("Failed to add all PBL chunks (%x/%x)\n", 31962306a36Sopenharmony_ci pbl_start, pbl_top - pbl_start); 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci pbl_chunk >>= 1; 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci pr_debug("added PBL chunk (%x/%x)\n", 32562306a36Sopenharmony_ci pbl_start, pbl_chunk); 32662306a36Sopenharmony_ci pbl_start += pbl_chunk; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_civoid c4iw_pblpool_destroy(struct c4iw_rdev *rdev) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci kref_put(&rdev->pbl_kref, destroy_pblpool); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/* 33962306a36Sopenharmony_ci * RQT Memory Manager. Uses Linux generic allocator. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci#define MIN_RQT_SHIFT 10 /* 1KB == min RQT size (16 entries) */ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciu32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6); 34762306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", (u32)addr, size << 6); 34862306a36Sopenharmony_ci if (!addr) 34962306a36Sopenharmony_ci pr_warn_ratelimited("%s: Out of RQT memory\n", 35062306a36Sopenharmony_ci pci_name(rdev->lldi.pdev)); 35162306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 35262306a36Sopenharmony_ci if (addr) { 35362306a36Sopenharmony_ci rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT); 35462306a36Sopenharmony_ci if (rdev->stats.rqt.cur > rdev->stats.rqt.max) 35562306a36Sopenharmony_ci rdev->stats.rqt.max = rdev->stats.rqt.cur; 35662306a36Sopenharmony_ci kref_get(&rdev->rqt_kref); 35762306a36Sopenharmony_ci } else 35862306a36Sopenharmony_ci rdev->stats.rqt.fail++; 35962306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 36062306a36Sopenharmony_ci return (u32)addr; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void destroy_rqtpool(struct kref *kref) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct c4iw_rdev *rdev; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci rdev = container_of(kref, struct c4iw_rdev, rqt_kref); 36862306a36Sopenharmony_ci gen_pool_destroy(rdev->rqt_pool); 36962306a36Sopenharmony_ci complete(&rdev->rqt_compl); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_civoid c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", addr, size << 6); 37562306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 37662306a36Sopenharmony_ci rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT); 37762306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 37862306a36Sopenharmony_ci gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6); 37962306a36Sopenharmony_ci kref_put(&rdev->rqt_kref, destroy_rqtpool); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint c4iw_rqtpool_create(struct c4iw_rdev *rdev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned rqt_start, rqt_chunk, rqt_top; 38562306a36Sopenharmony_ci int skip = 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1); 38862306a36Sopenharmony_ci if (!rdev->rqt_pool) 38962306a36Sopenharmony_ci return -ENOMEM; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * If SRQs are supported, then never use the first RQE from 39362306a36Sopenharmony_ci * the RQT region. This is because HW uses RQT index 0 as NULL. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci if (rdev->lldi.vr->srq.size) 39662306a36Sopenharmony_ci skip = T4_RQT_ENTRY_SIZE; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci rqt_start = rdev->lldi.vr->rq.start + skip; 39962306a36Sopenharmony_ci rqt_chunk = rdev->lldi.vr->rq.size - skip; 40062306a36Sopenharmony_ci rqt_top = rqt_start + rqt_chunk; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci while (rqt_start < rqt_top) { 40362306a36Sopenharmony_ci rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk); 40462306a36Sopenharmony_ci if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) { 40562306a36Sopenharmony_ci pr_debug("failed to add RQT chunk (%x/%x)\n", 40662306a36Sopenharmony_ci rqt_start, rqt_chunk); 40762306a36Sopenharmony_ci if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) { 40862306a36Sopenharmony_ci pr_warn("Failed to add all RQT chunks (%x/%x)\n", 40962306a36Sopenharmony_ci rqt_start, rqt_top - rqt_start); 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci rqt_chunk >>= 1; 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci pr_debug("added RQT chunk (%x/%x)\n", 41562306a36Sopenharmony_ci rqt_start, rqt_chunk); 41662306a36Sopenharmony_ci rqt_start += rqt_chunk; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid c4iw_rqtpool_destroy(struct c4iw_rdev *rdev) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci kref_put(&rdev->rqt_kref, destroy_rqtpool); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciint c4iw_alloc_srq_idx(struct c4iw_rdev *rdev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci int idx; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci idx = c4iw_id_alloc(&rdev->resource.srq_table); 43262306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 43362306a36Sopenharmony_ci if (idx == -1) { 43462306a36Sopenharmony_ci rdev->stats.srqt.fail++; 43562306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 43662306a36Sopenharmony_ci return -ENOMEM; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci rdev->stats.srqt.cur++; 43962306a36Sopenharmony_ci if (rdev->stats.srqt.cur > rdev->stats.srqt.max) 44062306a36Sopenharmony_ci rdev->stats.srqt.max = rdev->stats.srqt.cur; 44162306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 44262306a36Sopenharmony_ci return idx; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_civoid c4iw_free_srq_idx(struct c4iw_rdev *rdev, int idx) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci c4iw_id_free(&rdev->resource.srq_table, idx); 44862306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 44962306a36Sopenharmony_ci rdev->stats.srqt.cur--; 45062306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * On-Chip QP Memory. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci#define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */ 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciu32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size); 46162306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", (u32)addr, size); 46262306a36Sopenharmony_ci if (addr) { 46362306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 46462306a36Sopenharmony_ci rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT); 46562306a36Sopenharmony_ci if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max) 46662306a36Sopenharmony_ci rdev->stats.ocqp.max = rdev->stats.ocqp.cur; 46762306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci return (u32)addr; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_civoid c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci pr_debug("addr 0x%x size %d\n", addr, size); 47562306a36Sopenharmony_ci mutex_lock(&rdev->stats.lock); 47662306a36Sopenharmony_ci rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT); 47762306a36Sopenharmony_ci mutex_unlock(&rdev->stats.lock); 47862306a36Sopenharmony_ci gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciint c4iw_ocqp_pool_create(struct c4iw_rdev *rdev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci unsigned start, chunk, top; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1); 48662306a36Sopenharmony_ci if (!rdev->ocqp_pool) 48762306a36Sopenharmony_ci return -ENOMEM; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci start = rdev->lldi.vr->ocq.start; 49062306a36Sopenharmony_ci chunk = rdev->lldi.vr->ocq.size; 49162306a36Sopenharmony_ci top = start + chunk; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci while (start < top) { 49462306a36Sopenharmony_ci chunk = min(top - start + 1, chunk); 49562306a36Sopenharmony_ci if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) { 49662306a36Sopenharmony_ci pr_debug("failed to add OCQP chunk (%x/%x)\n", 49762306a36Sopenharmony_ci start, chunk); 49862306a36Sopenharmony_ci if (chunk <= 1024 << MIN_OCQP_SHIFT) { 49962306a36Sopenharmony_ci pr_warn("Failed to add all OCQP chunks (%x/%x)\n", 50062306a36Sopenharmony_ci start, top - start); 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci chunk >>= 1; 50462306a36Sopenharmony_ci } else { 50562306a36Sopenharmony_ci pr_debug("added OCQP chunk (%x/%x)\n", 50662306a36Sopenharmony_ci start, chunk); 50762306a36Sopenharmony_ci start += chunk; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_civoid c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci gen_pool_destroy(rdev->ocqp_pool); 51662306a36Sopenharmony_ci} 517