Path: blob/master/drivers/infiniband/hw/cxgb4/resource.c
15112 views
/*1* Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved.2*3* This software is available to you under a choice of one of two4* licenses. You may choose to be licensed under the terms of the GNU5* General Public License (GPL) Version 2, available from the file6* COPYING in the main directory of this source tree, or the7* OpenIB.org BSD license below:8*9* Redistribution and use in source and binary forms, with or10* without modification, are permitted provided that the following11* conditions are met:12*13* - Redistributions of source code must retain the above14* copyright notice, this list of conditions and the following15* disclaimer.16*17* - Redistributions in binary form must reproduce the above18* copyright notice, this list of conditions and the following19* disclaimer in the documentation and/or other materials20* provided with the distribution.21*22* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,23* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF24* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND25* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS26* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN27* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN28* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE29* SOFTWARE.30*/31/* Crude resource management */32#include <linux/kernel.h>33#include <linux/random.h>34#include <linux/slab.h>35#include <linux/kfifo.h>36#include <linux/spinlock.h>37#include <linux/errno.h>38#include <linux/genalloc.h>39#include "iw_cxgb4.h"4041#define RANDOM_SIZE 164243static int __c4iw_init_resource_fifo(struct kfifo *fifo,44spinlock_t *fifo_lock,45u32 nr, u32 skip_low,46u32 skip_high,47int random)48{49u32 i, j, entry = 0, idx;50u32 random_bytes;51u32 rarray[16];52spin_lock_init(fifo_lock);5354if (kfifo_alloc(fifo, nr * sizeof(u32), GFP_KERNEL))55return -ENOMEM;5657for (i = 0; i < skip_low + skip_high; i++)58kfifo_in(fifo, (unsigned char *) &entry, sizeof(u32));59if (random) {60j = 0;61random_bytes = random32();62for (i = 0; i < RANDOM_SIZE; i++)63rarray[i] = i + skip_low;64for (i = skip_low + RANDOM_SIZE; i < nr - skip_high; i++) {65if (j >= RANDOM_SIZE) {66j = 0;67random_bytes = random32();68}69idx = (random_bytes >> (j * 2)) & 0xF;70kfifo_in(fifo,71(unsigned char *) &rarray[idx],72sizeof(u32));73rarray[idx] = i;74j++;75}76for (i = 0; i < RANDOM_SIZE; i++)77kfifo_in(fifo,78(unsigned char *) &rarray[i],79sizeof(u32));80} else81for (i = skip_low; i < nr - skip_high; i++)82kfifo_in(fifo, (unsigned char *) &i, sizeof(u32));8384for (i = 0; i < skip_low + skip_high; i++)85if (kfifo_out_locked(fifo, (unsigned char *) &entry,86sizeof(u32), fifo_lock))87break;88return 0;89}9091static int c4iw_init_resource_fifo(struct kfifo *fifo, spinlock_t * fifo_lock,92u32 nr, u32 skip_low, u32 skip_high)93{94return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low,95skip_high, 0);96}9798static int c4iw_init_resource_fifo_random(struct kfifo *fifo,99spinlock_t *fifo_lock,100u32 nr, u32 skip_low, u32 skip_high)101{102return __c4iw_init_resource_fifo(fifo, fifo_lock, nr, skip_low,103skip_high, 1);104}105106static int c4iw_init_qid_fifo(struct c4iw_rdev *rdev)107{108u32 i;109110spin_lock_init(&rdev->resource.qid_fifo_lock);111112if (kfifo_alloc(&rdev->resource.qid_fifo, rdev->lldi.vr->qp.size *113sizeof(u32), GFP_KERNEL))114return -ENOMEM;115116for (i = rdev->lldi.vr->qp.start;117i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)118if (!(i & rdev->qpmask))119kfifo_in(&rdev->resource.qid_fifo,120(unsigned char *) &i, sizeof(u32));121return 0;122}123124/* nr_* must be power of 2 */125int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid)126{127int err = 0;128err = c4iw_init_resource_fifo_random(&rdev->resource.tpt_fifo,129&rdev->resource.tpt_fifo_lock,130nr_tpt, 1, 0);131if (err)132goto tpt_err;133err = c4iw_init_qid_fifo(rdev);134if (err)135goto qid_err;136err = c4iw_init_resource_fifo(&rdev->resource.pdid_fifo,137&rdev->resource.pdid_fifo_lock,138nr_pdid, 1, 0);139if (err)140goto pdid_err;141return 0;142pdid_err:143kfifo_free(&rdev->resource.qid_fifo);144qid_err:145kfifo_free(&rdev->resource.tpt_fifo);146tpt_err:147return -ENOMEM;148}149150/*151* returns 0 if no resource available152*/153u32 c4iw_get_resource(struct kfifo *fifo, spinlock_t *lock)154{155u32 entry;156if (kfifo_out_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock))157return entry;158else159return 0;160}161162void c4iw_put_resource(struct kfifo *fifo, u32 entry, spinlock_t *lock)163{164PDBG("%s entry 0x%x\n", __func__, entry);165kfifo_in_locked(fifo, (unsigned char *) &entry, sizeof(u32), lock);166}167168u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)169{170struct c4iw_qid_list *entry;171u32 qid;172int i;173174mutex_lock(&uctx->lock);175if (!list_empty(&uctx->cqids)) {176entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,177entry);178list_del(&entry->entry);179qid = entry->qid;180kfree(entry);181} else {182qid = c4iw_get_resource(&rdev->resource.qid_fifo,183&rdev->resource.qid_fifo_lock);184if (!qid)185goto out;186for (i = qid+1; i & rdev->qpmask; i++) {187entry = kmalloc(sizeof *entry, GFP_KERNEL);188if (!entry)189goto out;190entry->qid = i;191list_add_tail(&entry->entry, &uctx->cqids);192}193194/*195* now put the same ids on the qp list since they all196* map to the same db/gts page.197*/198entry = kmalloc(sizeof *entry, GFP_KERNEL);199if (!entry)200goto out;201entry->qid = qid;202list_add_tail(&entry->entry, &uctx->qpids);203for (i = qid+1; i & rdev->qpmask; i++) {204entry = kmalloc(sizeof *entry, GFP_KERNEL);205if (!entry)206goto out;207entry->qid = i;208list_add_tail(&entry->entry, &uctx->qpids);209}210}211out:212mutex_unlock(&uctx->lock);213PDBG("%s qid 0x%x\n", __func__, qid);214return qid;215}216217void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,218struct c4iw_dev_ucontext *uctx)219{220struct c4iw_qid_list *entry;221222entry = kmalloc(sizeof *entry, GFP_KERNEL);223if (!entry)224return;225PDBG("%s qid 0x%x\n", __func__, qid);226entry->qid = qid;227mutex_lock(&uctx->lock);228list_add_tail(&entry->entry, &uctx->cqids);229mutex_unlock(&uctx->lock);230}231232u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)233{234struct c4iw_qid_list *entry;235u32 qid;236int i;237238mutex_lock(&uctx->lock);239if (!list_empty(&uctx->qpids)) {240entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,241entry);242list_del(&entry->entry);243qid = entry->qid;244kfree(entry);245} else {246qid = c4iw_get_resource(&rdev->resource.qid_fifo,247&rdev->resource.qid_fifo_lock);248if (!qid)249goto out;250for (i = qid+1; i & rdev->qpmask; i++) {251entry = kmalloc(sizeof *entry, GFP_KERNEL);252if (!entry)253goto out;254entry->qid = i;255list_add_tail(&entry->entry, &uctx->qpids);256}257258/*259* now put the same ids on the cq list since they all260* map to the same db/gts page.261*/262entry = kmalloc(sizeof *entry, GFP_KERNEL);263if (!entry)264goto out;265entry->qid = qid;266list_add_tail(&entry->entry, &uctx->cqids);267for (i = qid; i & rdev->qpmask; i++) {268entry = kmalloc(sizeof *entry, GFP_KERNEL);269if (!entry)270goto out;271entry->qid = i;272list_add_tail(&entry->entry, &uctx->cqids);273}274}275out:276mutex_unlock(&uctx->lock);277PDBG("%s qid 0x%x\n", __func__, qid);278return qid;279}280281void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,282struct c4iw_dev_ucontext *uctx)283{284struct c4iw_qid_list *entry;285286entry = kmalloc(sizeof *entry, GFP_KERNEL);287if (!entry)288return;289PDBG("%s qid 0x%x\n", __func__, qid);290entry->qid = qid;291mutex_lock(&uctx->lock);292list_add_tail(&entry->entry, &uctx->qpids);293mutex_unlock(&uctx->lock);294}295296void c4iw_destroy_resource(struct c4iw_resource *rscp)297{298kfifo_free(&rscp->tpt_fifo);299kfifo_free(&rscp->qid_fifo);300kfifo_free(&rscp->pdid_fifo);301}302303/*304* PBL Memory Manager. Uses Linux generic allocator.305*/306307#define MIN_PBL_SHIFT 8 /* 256B == min PBL size (32 entries) */308309u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)310{311unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);312PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);313if (!addr && printk_ratelimit())314printk(KERN_WARNING MOD "%s: Out of PBL memory\n",315pci_name(rdev->lldi.pdev));316return (u32)addr;317}318319void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)320{321PDBG("%s addr 0x%x size %d\n", __func__, addr, size);322gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);323}324325int c4iw_pblpool_create(struct c4iw_rdev *rdev)326{327unsigned pbl_start, pbl_chunk, pbl_top;328329rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1);330if (!rdev->pbl_pool)331return -ENOMEM;332333pbl_start = rdev->lldi.vr->pbl.start;334pbl_chunk = rdev->lldi.vr->pbl.size;335pbl_top = pbl_start + pbl_chunk;336337while (pbl_start < pbl_top) {338pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk);339if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) {340PDBG("%s failed to add PBL chunk (%x/%x)\n",341__func__, pbl_start, pbl_chunk);342if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {343printk(KERN_WARNING MOD344"Failed to add all PBL chunks (%x/%x)\n",345pbl_start,346pbl_top - pbl_start);347return 0;348}349pbl_chunk >>= 1;350} else {351PDBG("%s added PBL chunk (%x/%x)\n",352__func__, pbl_start, pbl_chunk);353pbl_start += pbl_chunk;354}355}356357return 0;358}359360void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)361{362gen_pool_destroy(rdev->pbl_pool);363}364365/*366* RQT Memory Manager. Uses Linux generic allocator.367*/368369#define MIN_RQT_SHIFT 10 /* 1KB == min RQT size (16 entries) */370371u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)372{373unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);374PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size << 6);375if (!addr && printk_ratelimit())376printk(KERN_WARNING MOD "%s: Out of RQT memory\n",377pci_name(rdev->lldi.pdev));378return (u32)addr;379}380381void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)382{383PDBG("%s addr 0x%x size %d\n", __func__, addr, size << 6);384gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);385}386387int c4iw_rqtpool_create(struct c4iw_rdev *rdev)388{389unsigned rqt_start, rqt_chunk, rqt_top;390391rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);392if (!rdev->rqt_pool)393return -ENOMEM;394395rqt_start = rdev->lldi.vr->rq.start;396rqt_chunk = rdev->lldi.vr->rq.size;397rqt_top = rqt_start + rqt_chunk;398399while (rqt_start < rqt_top) {400rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);401if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {402PDBG("%s failed to add RQT chunk (%x/%x)\n",403__func__, rqt_start, rqt_chunk);404if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {405printk(KERN_WARNING MOD406"Failed to add all RQT chunks (%x/%x)\n",407rqt_start, rqt_top - rqt_start);408return 0;409}410rqt_chunk >>= 1;411} else {412PDBG("%s added RQT chunk (%x/%x)\n",413__func__, rqt_start, rqt_chunk);414rqt_start += rqt_chunk;415}416}417return 0;418}419420void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)421{422gen_pool_destroy(rdev->rqt_pool);423}424425/*426* On-Chip QP Memory.427*/428#define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */429430u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)431{432unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);433PDBG("%s addr 0x%x size %d\n", __func__, (u32)addr, size);434return (u32)addr;435}436437void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)438{439PDBG("%s addr 0x%x size %d\n", __func__, addr, size);440gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);441}442443int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)444{445unsigned start, chunk, top;446447rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);448if (!rdev->ocqp_pool)449return -ENOMEM;450451start = rdev->lldi.vr->ocq.start;452chunk = rdev->lldi.vr->ocq.size;453top = start + chunk;454455while (start < top) {456chunk = min(top - start + 1, chunk);457if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {458PDBG("%s failed to add OCQP chunk (%x/%x)\n",459__func__, start, chunk);460if (chunk <= 1024 << MIN_OCQP_SHIFT) {461printk(KERN_WARNING MOD462"Failed to add all OCQP chunks (%x/%x)\n",463start, top - start);464return 0;465}466chunk >>= 1;467} else {468PDBG("%s added OCQP chunk (%x/%x)\n",469__func__, start, chunk);470start += chunk;471}472}473return 0;474}475476void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)477{478gen_pool_destroy(rdev->ocqp_pool);479}480481482