cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

resource.c (13709B)


      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,
     57		       u32 nr_pdid, u32 nr_srqt)
     58{
     59	int err = 0;
     60	err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1,
     61					C4IW_ID_TABLE_F_RANDOM);
     62	if (err)
     63		goto tpt_err;
     64	err = c4iw_init_qid_table(rdev);
     65	if (err)
     66		goto qid_err;
     67	err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0,
     68					nr_pdid, 1, 0);
     69	if (err)
     70		goto pdid_err;
     71	if (!nr_srqt)
     72		err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
     73					  1, 1, 0);
     74	else
     75		err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
     76					  nr_srqt, 0, 0);
     77	if (err)
     78		goto srq_err;
     79	return 0;
     80 srq_err:
     81	c4iw_id_table_free(&rdev->resource.pdid_table);
     82 pdid_err:
     83	c4iw_id_table_free(&rdev->resource.qid_table);
     84 qid_err:
     85	c4iw_id_table_free(&rdev->resource.tpt_table);
     86 tpt_err:
     87	return -ENOMEM;
     88}
     89
     90/*
     91 * returns 0 if no resource available
     92 */
     93u32 c4iw_get_resource(struct c4iw_id_table *id_table)
     94{
     95	u32 entry;
     96	entry = c4iw_id_alloc(id_table);
     97	if (entry == (u32)(-1))
     98		return 0;
     99	return entry;
    100}
    101
    102void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry)
    103{
    104	pr_debug("entry 0x%x\n", entry);
    105	c4iw_id_free(id_table, entry);
    106}
    107
    108u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
    109{
    110	struct c4iw_qid_list *entry;
    111	u32 qid;
    112	int i;
    113
    114	mutex_lock(&uctx->lock);
    115	if (!list_empty(&uctx->cqids)) {
    116		entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,
    117				   entry);
    118		list_del(&entry->entry);
    119		qid = entry->qid;
    120		kfree(entry);
    121	} else {
    122		qid = c4iw_get_resource(&rdev->resource.qid_table);
    123		if (!qid)
    124			goto out;
    125		mutex_lock(&rdev->stats.lock);
    126		rdev->stats.qid.cur += rdev->qpmask + 1;
    127		mutex_unlock(&rdev->stats.lock);
    128		for (i = qid+1; i & rdev->qpmask; i++) {
    129			entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    130			if (!entry)
    131				goto out;
    132			entry->qid = i;
    133			list_add_tail(&entry->entry, &uctx->cqids);
    134		}
    135
    136		/*
    137		 * now put the same ids on the qp list since they all
    138		 * map to the same db/gts page.
    139		 */
    140		entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    141		if (!entry)
    142			goto out;
    143		entry->qid = qid;
    144		list_add_tail(&entry->entry, &uctx->qpids);
    145		for (i = qid+1; i & rdev->qpmask; i++) {
    146			entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    147			if (!entry)
    148				goto out;
    149			entry->qid = i;
    150			list_add_tail(&entry->entry, &uctx->qpids);
    151		}
    152	}
    153out:
    154	mutex_unlock(&uctx->lock);
    155	pr_debug("qid 0x%x\n", qid);
    156	mutex_lock(&rdev->stats.lock);
    157	if (rdev->stats.qid.cur > rdev->stats.qid.max)
    158		rdev->stats.qid.max = rdev->stats.qid.cur;
    159	mutex_unlock(&rdev->stats.lock);
    160	return qid;
    161}
    162
    163void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,
    164		   struct c4iw_dev_ucontext *uctx)
    165{
    166	struct c4iw_qid_list *entry;
    167
    168	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    169	if (!entry)
    170		return;
    171	pr_debug("qid 0x%x\n", qid);
    172	entry->qid = qid;
    173	mutex_lock(&uctx->lock);
    174	list_add_tail(&entry->entry, &uctx->cqids);
    175	mutex_unlock(&uctx->lock);
    176}
    177
    178u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
    179{
    180	struct c4iw_qid_list *entry;
    181	u32 qid;
    182	int i;
    183
    184	mutex_lock(&uctx->lock);
    185	if (!list_empty(&uctx->qpids)) {
    186		entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,
    187				   entry);
    188		list_del(&entry->entry);
    189		qid = entry->qid;
    190		kfree(entry);
    191	} else {
    192		qid = c4iw_get_resource(&rdev->resource.qid_table);
    193		if (!qid) {
    194			mutex_lock(&rdev->stats.lock);
    195			rdev->stats.qid.fail++;
    196			mutex_unlock(&rdev->stats.lock);
    197			goto out;
    198		}
    199		mutex_lock(&rdev->stats.lock);
    200		rdev->stats.qid.cur += rdev->qpmask + 1;
    201		mutex_unlock(&rdev->stats.lock);
    202		for (i = qid+1; i & rdev->qpmask; i++) {
    203			entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    204			if (!entry)
    205				goto out;
    206			entry->qid = i;
    207			list_add_tail(&entry->entry, &uctx->qpids);
    208		}
    209
    210		/*
    211		 * now put the same ids on the cq list since they all
    212		 * map to the same db/gts page.
    213		 */
    214		entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    215		if (!entry)
    216			goto out;
    217		entry->qid = qid;
    218		list_add_tail(&entry->entry, &uctx->cqids);
    219		for (i = qid + 1; i & rdev->qpmask; i++) {
    220			entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    221			if (!entry)
    222				goto out;
    223			entry->qid = i;
    224			list_add_tail(&entry->entry, &uctx->cqids);
    225		}
    226	}
    227out:
    228	mutex_unlock(&uctx->lock);
    229	pr_debug("qid 0x%x\n", qid);
    230	mutex_lock(&rdev->stats.lock);
    231	if (rdev->stats.qid.cur > rdev->stats.qid.max)
    232		rdev->stats.qid.max = rdev->stats.qid.cur;
    233	mutex_unlock(&rdev->stats.lock);
    234	return qid;
    235}
    236
    237void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,
    238		   struct c4iw_dev_ucontext *uctx)
    239{
    240	struct c4iw_qid_list *entry;
    241
    242	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
    243	if (!entry)
    244		return;
    245	pr_debug("qid 0x%x\n", qid);
    246	entry->qid = qid;
    247	mutex_lock(&uctx->lock);
    248	list_add_tail(&entry->entry, &uctx->qpids);
    249	mutex_unlock(&uctx->lock);
    250}
    251
    252void c4iw_destroy_resource(struct c4iw_resource *rscp)
    253{
    254	c4iw_id_table_free(&rscp->tpt_table);
    255	c4iw_id_table_free(&rscp->qid_table);
    256	c4iw_id_table_free(&rscp->pdid_table);
    257}
    258
    259/*
    260 * PBL Memory Manager.  Uses Linux generic allocator.
    261 */
    262
    263#define MIN_PBL_SHIFT 8			/* 256B == min PBL size (32 entries) */
    264
    265u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
    266{
    267	unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
    268	pr_debug("addr 0x%x size %d\n", (u32)addr, size);
    269	mutex_lock(&rdev->stats.lock);
    270	if (addr) {
    271		rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT);
    272		if (rdev->stats.pbl.cur > rdev->stats.pbl.max)
    273			rdev->stats.pbl.max = rdev->stats.pbl.cur;
    274		kref_get(&rdev->pbl_kref);
    275	} else
    276		rdev->stats.pbl.fail++;
    277	mutex_unlock(&rdev->stats.lock);
    278	return (u32)addr;
    279}
    280
    281static void destroy_pblpool(struct kref *kref)
    282{
    283	struct c4iw_rdev *rdev;
    284
    285	rdev = container_of(kref, struct c4iw_rdev, pbl_kref);
    286	gen_pool_destroy(rdev->pbl_pool);
    287	complete(&rdev->pbl_compl);
    288}
    289
    290void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
    291{
    292	pr_debug("addr 0x%x size %d\n", addr, size);
    293	mutex_lock(&rdev->stats.lock);
    294	rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT);
    295	mutex_unlock(&rdev->stats.lock);
    296	gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);
    297	kref_put(&rdev->pbl_kref, destroy_pblpool);
    298}
    299
    300int c4iw_pblpool_create(struct c4iw_rdev *rdev)
    301{
    302	unsigned pbl_start, pbl_chunk, pbl_top;
    303
    304	rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1);
    305	if (!rdev->pbl_pool)
    306		return -ENOMEM;
    307
    308	pbl_start = rdev->lldi.vr->pbl.start;
    309	pbl_chunk = rdev->lldi.vr->pbl.size;
    310	pbl_top = pbl_start + pbl_chunk;
    311
    312	while (pbl_start < pbl_top) {
    313		pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk);
    314		if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) {
    315			pr_debug("failed to add PBL chunk (%x/%x)\n",
    316				 pbl_start, pbl_chunk);
    317			if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {
    318				pr_warn("Failed to add all PBL chunks (%x/%x)\n",
    319					pbl_start, pbl_top - pbl_start);
    320				return 0;
    321			}
    322			pbl_chunk >>= 1;
    323		} else {
    324			pr_debug("added PBL chunk (%x/%x)\n",
    325				 pbl_start, pbl_chunk);
    326			pbl_start += pbl_chunk;
    327		}
    328	}
    329
    330	return 0;
    331}
    332
    333void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
    334{
    335	kref_put(&rdev->pbl_kref, destroy_pblpool);
    336}
    337
    338/*
    339 * RQT Memory Manager.  Uses Linux generic allocator.
    340 */
    341
    342#define MIN_RQT_SHIFT 10	/* 1KB == min RQT size (16 entries) */
    343
    344u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
    345{
    346	unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
    347	pr_debug("addr 0x%x size %d\n", (u32)addr, size << 6);
    348	if (!addr)
    349		pr_warn_ratelimited("%s: Out of RQT memory\n",
    350				    pci_name(rdev->lldi.pdev));
    351	mutex_lock(&rdev->stats.lock);
    352	if (addr) {
    353		rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
    354		if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
    355			rdev->stats.rqt.max = rdev->stats.rqt.cur;
    356		kref_get(&rdev->rqt_kref);
    357	} else
    358		rdev->stats.rqt.fail++;
    359	mutex_unlock(&rdev->stats.lock);
    360	return (u32)addr;
    361}
    362
    363static void destroy_rqtpool(struct kref *kref)
    364{
    365	struct c4iw_rdev *rdev;
    366
    367	rdev = container_of(kref, struct c4iw_rdev, rqt_kref);
    368	gen_pool_destroy(rdev->rqt_pool);
    369	complete(&rdev->rqt_compl);
    370}
    371
    372void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
    373{
    374	pr_debug("addr 0x%x size %d\n", addr, size << 6);
    375	mutex_lock(&rdev->stats.lock);
    376	rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
    377	mutex_unlock(&rdev->stats.lock);
    378	gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
    379	kref_put(&rdev->rqt_kref, destroy_rqtpool);
    380}
    381
    382int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
    383{
    384	unsigned rqt_start, rqt_chunk, rqt_top;
    385	int skip = 0;
    386
    387	rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);
    388	if (!rdev->rqt_pool)
    389		return -ENOMEM;
    390
    391	/*
    392	 * If SRQs are supported, then never use the first RQE from
    393	 * the RQT region. This is because HW uses RQT index 0 as NULL.
    394	 */
    395	if (rdev->lldi.vr->srq.size)
    396		skip = T4_RQT_ENTRY_SIZE;
    397
    398	rqt_start = rdev->lldi.vr->rq.start + skip;
    399	rqt_chunk = rdev->lldi.vr->rq.size - skip;
    400	rqt_top = rqt_start + rqt_chunk;
    401
    402	while (rqt_start < rqt_top) {
    403		rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);
    404		if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {
    405			pr_debug("failed to add RQT chunk (%x/%x)\n",
    406				 rqt_start, rqt_chunk);
    407			if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {
    408				pr_warn("Failed to add all RQT chunks (%x/%x)\n",
    409					rqt_start, rqt_top - rqt_start);
    410				return 0;
    411			}
    412			rqt_chunk >>= 1;
    413		} else {
    414			pr_debug("added RQT chunk (%x/%x)\n",
    415				 rqt_start, rqt_chunk);
    416			rqt_start += rqt_chunk;
    417		}
    418	}
    419	return 0;
    420}
    421
    422void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
    423{
    424	kref_put(&rdev->rqt_kref, destroy_rqtpool);
    425}
    426
    427int c4iw_alloc_srq_idx(struct c4iw_rdev *rdev)
    428{
    429	int idx;
    430
    431	idx = c4iw_id_alloc(&rdev->resource.srq_table);
    432	mutex_lock(&rdev->stats.lock);
    433	if (idx == -1) {
    434		rdev->stats.srqt.fail++;
    435		mutex_unlock(&rdev->stats.lock);
    436		return -ENOMEM;
    437	}
    438	rdev->stats.srqt.cur++;
    439	if (rdev->stats.srqt.cur > rdev->stats.srqt.max)
    440		rdev->stats.srqt.max = rdev->stats.srqt.cur;
    441	mutex_unlock(&rdev->stats.lock);
    442	return idx;
    443}
    444
    445void c4iw_free_srq_idx(struct c4iw_rdev *rdev, int idx)
    446{
    447	c4iw_id_free(&rdev->resource.srq_table, idx);
    448	mutex_lock(&rdev->stats.lock);
    449	rdev->stats.srqt.cur--;
    450	mutex_unlock(&rdev->stats.lock);
    451}
    452
    453/*
    454 * On-Chip QP Memory.
    455 */
    456#define MIN_OCQP_SHIFT 12	/* 4KB == min ocqp size */
    457
    458u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
    459{
    460	unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
    461	pr_debug("addr 0x%x size %d\n", (u32)addr, size);
    462	if (addr) {
    463		mutex_lock(&rdev->stats.lock);
    464		rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT);
    465		if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max)
    466			rdev->stats.ocqp.max = rdev->stats.ocqp.cur;
    467		mutex_unlock(&rdev->stats.lock);
    468	}
    469	return (u32)addr;
    470}
    471
    472void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
    473{
    474	pr_debug("addr 0x%x size %d\n", addr, size);
    475	mutex_lock(&rdev->stats.lock);
    476	rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT);
    477	mutex_unlock(&rdev->stats.lock);
    478	gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
    479}
    480
    481int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
    482{
    483	unsigned start, chunk, top;
    484
    485	rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
    486	if (!rdev->ocqp_pool)
    487		return -ENOMEM;
    488
    489	start = rdev->lldi.vr->ocq.start;
    490	chunk = rdev->lldi.vr->ocq.size;
    491	top = start + chunk;
    492
    493	while (start < top) {
    494		chunk = min(top - start + 1, chunk);
    495		if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
    496			pr_debug("failed to add OCQP chunk (%x/%x)\n",
    497				 start, chunk);
    498			if (chunk <= 1024 << MIN_OCQP_SHIFT) {
    499				pr_warn("Failed to add all OCQP chunks (%x/%x)\n",
    500					start, top - start);
    501				return 0;
    502			}
    503			chunk >>= 1;
    504		} else {
    505			pr_debug("added OCQP chunk (%x/%x)\n",
    506				 start, chunk);
    507			start += chunk;
    508		}
    509	}
    510	return 0;
    511}
    512
    513void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
    514{
    515	gen_pool_destroy(rdev->ocqp_pool);
    516}