18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2005 Cisco Systems.  All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
78c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
138c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
148c2ecf20Sopenharmony_ci *     conditions are met:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
178c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
188c2ecf20Sopenharmony_ci *        disclaimer.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
218c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
228c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
238c2ecf20Sopenharmony_ci *        provided with the distribution.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
328c2ecf20Sopenharmony_ci * SOFTWARE.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/mm.h>
368c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
378c2ecf20Sopenharmony_ci#include <linux/sched.h>
388c2ecf20Sopenharmony_ci#include <linux/slab.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <asm/page.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include "mthca_memfree.h"
438c2ecf20Sopenharmony_ci#include "mthca_dev.h"
448c2ecf20Sopenharmony_ci#include "mthca_cmd.h"
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * We allocate in as big chunks as we can, up to a maximum of 256 KB
488c2ecf20Sopenharmony_ci * per chunk.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cienum {
518c2ecf20Sopenharmony_ci	MTHCA_ICM_ALLOC_SIZE   = 1 << 18,
528c2ecf20Sopenharmony_ci	MTHCA_TABLE_CHUNK_SIZE = 1 << 18
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct mthca_user_db_table {
568c2ecf20Sopenharmony_ci	struct mutex mutex;
578c2ecf20Sopenharmony_ci	struct {
588c2ecf20Sopenharmony_ci		u64                uvirt;
598c2ecf20Sopenharmony_ci		struct scatterlist mem;
608c2ecf20Sopenharmony_ci		int                refcount;
618c2ecf20Sopenharmony_ci	} page[];
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void mthca_free_icm_pages(struct mthca_dev *dev, struct mthca_icm_chunk *chunk)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int i;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (chunk->nsg > 0)
698c2ecf20Sopenharmony_ci		pci_unmap_sg(dev->pdev, chunk->mem, chunk->npages,
708c2ecf20Sopenharmony_ci			     PCI_DMA_BIDIRECTIONAL);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	for (i = 0; i < chunk->npages; ++i)
738c2ecf20Sopenharmony_ci		__free_pages(sg_page(&chunk->mem[i]),
748c2ecf20Sopenharmony_ci			     get_order(chunk->mem[i].length));
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void mthca_free_icm_coherent(struct mthca_dev *dev, struct mthca_icm_chunk *chunk)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < chunk->npages; ++i) {
828c2ecf20Sopenharmony_ci		dma_free_coherent(&dev->pdev->dev, chunk->mem[i].length,
838c2ecf20Sopenharmony_ci				  lowmem_page_address(sg_page(&chunk->mem[i])),
848c2ecf20Sopenharmony_ci				  sg_dma_address(&chunk->mem[i]));
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid mthca_free_icm(struct mthca_dev *dev, struct mthca_icm *icm, int coherent)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct mthca_icm_chunk *chunk, *tmp;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!icm)
938c2ecf20Sopenharmony_ci		return;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) {
968c2ecf20Sopenharmony_ci		if (coherent)
978c2ecf20Sopenharmony_ci			mthca_free_icm_coherent(dev, chunk);
988c2ecf20Sopenharmony_ci		else
998c2ecf20Sopenharmony_ci			mthca_free_icm_pages(dev, chunk);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		kfree(chunk);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	kfree(icm);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int mthca_alloc_icm_pages(struct scatterlist *mem, int order, gfp_t gfp_mask)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct page *page;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/*
1128c2ecf20Sopenharmony_ci	 * Use __GFP_ZERO because buggy firmware assumes ICM pages are
1138c2ecf20Sopenharmony_ci	 * cleared, and subtle failures are seen if they aren't.
1148c2ecf20Sopenharmony_ci	 */
1158c2ecf20Sopenharmony_ci	page = alloc_pages(gfp_mask | __GFP_ZERO, order);
1168c2ecf20Sopenharmony_ci	if (!page)
1178c2ecf20Sopenharmony_ci		return -ENOMEM;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	sg_set_page(mem, page, PAGE_SIZE << order, 0);
1208c2ecf20Sopenharmony_ci	return 0;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int mthca_alloc_icm_coherent(struct device *dev, struct scatterlist *mem,
1248c2ecf20Sopenharmony_ci				    int order, gfp_t gfp_mask)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	void *buf = dma_alloc_coherent(dev, PAGE_SIZE << order, &sg_dma_address(mem),
1278c2ecf20Sopenharmony_ci				       gfp_mask);
1288c2ecf20Sopenharmony_ci	if (!buf)
1298c2ecf20Sopenharmony_ci		return -ENOMEM;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	sg_set_buf(mem, buf, PAGE_SIZE << order);
1328c2ecf20Sopenharmony_ci	BUG_ON(mem->offset);
1338c2ecf20Sopenharmony_ci	sg_dma_len(mem) = PAGE_SIZE << order;
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistruct mthca_icm *mthca_alloc_icm(struct mthca_dev *dev, int npages,
1388c2ecf20Sopenharmony_ci				  gfp_t gfp_mask, int coherent)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct mthca_icm *icm;
1418c2ecf20Sopenharmony_ci	struct mthca_icm_chunk *chunk = NULL;
1428c2ecf20Sopenharmony_ci	int cur_order;
1438c2ecf20Sopenharmony_ci	int ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* We use sg_set_buf for coherent allocs, which assumes low memory */
1468c2ecf20Sopenharmony_ci	BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM));
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
1498c2ecf20Sopenharmony_ci	if (!icm)
1508c2ecf20Sopenharmony_ci		return icm;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	icm->refcount = 0;
1538c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&icm->chunk_list);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	cur_order = get_order(MTHCA_ICM_ALLOC_SIZE);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	while (npages > 0) {
1588c2ecf20Sopenharmony_ci		if (!chunk) {
1598c2ecf20Sopenharmony_ci			chunk = kmalloc(sizeof *chunk,
1608c2ecf20Sopenharmony_ci					gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
1618c2ecf20Sopenharmony_ci			if (!chunk)
1628c2ecf20Sopenharmony_ci				goto fail;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci			sg_init_table(chunk->mem, MTHCA_ICM_CHUNK_LEN);
1658c2ecf20Sopenharmony_ci			chunk->npages = 0;
1668c2ecf20Sopenharmony_ci			chunk->nsg    = 0;
1678c2ecf20Sopenharmony_ci			list_add_tail(&chunk->list, &icm->chunk_list);
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		while (1 << cur_order > npages)
1718c2ecf20Sopenharmony_ci			--cur_order;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		if (coherent)
1748c2ecf20Sopenharmony_ci			ret = mthca_alloc_icm_coherent(&dev->pdev->dev,
1758c2ecf20Sopenharmony_ci						       &chunk->mem[chunk->npages],
1768c2ecf20Sopenharmony_ci						       cur_order, gfp_mask);
1778c2ecf20Sopenharmony_ci		else
1788c2ecf20Sopenharmony_ci			ret = mthca_alloc_icm_pages(&chunk->mem[chunk->npages],
1798c2ecf20Sopenharmony_ci						    cur_order, gfp_mask);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		if (!ret) {
1828c2ecf20Sopenharmony_ci			++chunk->npages;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci			if (coherent)
1858c2ecf20Sopenharmony_ci				++chunk->nsg;
1868c2ecf20Sopenharmony_ci			else if (chunk->npages == MTHCA_ICM_CHUNK_LEN) {
1878c2ecf20Sopenharmony_ci				chunk->nsg = pci_map_sg(dev->pdev, chunk->mem,
1888c2ecf20Sopenharmony_ci							chunk->npages,
1898c2ecf20Sopenharmony_ci							PCI_DMA_BIDIRECTIONAL);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci				if (chunk->nsg <= 0)
1928c2ecf20Sopenharmony_ci					goto fail;
1938c2ecf20Sopenharmony_ci			}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			if (chunk->npages == MTHCA_ICM_CHUNK_LEN)
1968c2ecf20Sopenharmony_ci				chunk = NULL;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci			npages -= 1 << cur_order;
1998c2ecf20Sopenharmony_ci		} else {
2008c2ecf20Sopenharmony_ci			--cur_order;
2018c2ecf20Sopenharmony_ci			if (cur_order < 0)
2028c2ecf20Sopenharmony_ci				goto fail;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (!coherent && chunk) {
2078c2ecf20Sopenharmony_ci		chunk->nsg = pci_map_sg(dev->pdev, chunk->mem,
2088c2ecf20Sopenharmony_ci					chunk->npages,
2098c2ecf20Sopenharmony_ci					PCI_DMA_BIDIRECTIONAL);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		if (chunk->nsg <= 0)
2128c2ecf20Sopenharmony_ci			goto fail;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return icm;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cifail:
2188c2ecf20Sopenharmony_ci	mthca_free_icm(dev, icm, coherent);
2198c2ecf20Sopenharmony_ci	return NULL;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ciint mthca_table_get(struct mthca_dev *dev, struct mthca_icm_table *table, int obj)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	int i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE;
2258c2ecf20Sopenharmony_ci	int ret = 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	mutex_lock(&table->mutex);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (table->icm[i]) {
2308c2ecf20Sopenharmony_ci		++table->icm[i]->refcount;
2318c2ecf20Sopenharmony_ci		goto out;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	table->icm[i] = mthca_alloc_icm(dev, MTHCA_TABLE_CHUNK_SIZE >> PAGE_SHIFT,
2358c2ecf20Sopenharmony_ci					(table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
2368c2ecf20Sopenharmony_ci					__GFP_NOWARN, table->coherent);
2378c2ecf20Sopenharmony_ci	if (!table->icm[i]) {
2388c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2398c2ecf20Sopenharmony_ci		goto out;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (mthca_MAP_ICM(dev, table->icm[i],
2438c2ecf20Sopenharmony_ci			  table->virt + i * MTHCA_TABLE_CHUNK_SIZE)) {
2448c2ecf20Sopenharmony_ci		mthca_free_icm(dev, table->icm[i], table->coherent);
2458c2ecf20Sopenharmony_ci		table->icm[i] = NULL;
2468c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2478c2ecf20Sopenharmony_ci		goto out;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	++table->icm[i]->refcount;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ciout:
2538c2ecf20Sopenharmony_ci	mutex_unlock(&table->mutex);
2548c2ecf20Sopenharmony_ci	return ret;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_civoid mthca_table_put(struct mthca_dev *dev, struct mthca_icm_table *table, int obj)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int i;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
2628c2ecf20Sopenharmony_ci		return;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	mutex_lock(&table->mutex);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (--table->icm[i]->refcount == 0) {
2698c2ecf20Sopenharmony_ci		mthca_UNMAP_ICM(dev, table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
2708c2ecf20Sopenharmony_ci				MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
2718c2ecf20Sopenharmony_ci		mthca_free_icm(dev, table->icm[i], table->coherent);
2728c2ecf20Sopenharmony_ci		table->icm[i] = NULL;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	mutex_unlock(&table->mutex);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_civoid *mthca_table_find(struct mthca_icm_table *table, int obj, dma_addr_t *dma_handle)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int idx, offset, dma_offset, i;
2818c2ecf20Sopenharmony_ci	struct mthca_icm_chunk *chunk;
2828c2ecf20Sopenharmony_ci	struct mthca_icm *icm;
2838c2ecf20Sopenharmony_ci	struct page *page = NULL;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (!table->lowmem)
2868c2ecf20Sopenharmony_ci		return NULL;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	mutex_lock(&table->mutex);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	idx = (obj & (table->num_obj - 1)) * table->obj_size;
2918c2ecf20Sopenharmony_ci	icm = table->icm[idx / MTHCA_TABLE_CHUNK_SIZE];
2928c2ecf20Sopenharmony_ci	dma_offset = offset = idx % MTHCA_TABLE_CHUNK_SIZE;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (!icm)
2958c2ecf20Sopenharmony_ci		goto out;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &icm->chunk_list, list) {
2988c2ecf20Sopenharmony_ci		for (i = 0; i < chunk->npages; ++i) {
2998c2ecf20Sopenharmony_ci			if (dma_handle && dma_offset >= 0) {
3008c2ecf20Sopenharmony_ci				if (sg_dma_len(&chunk->mem[i]) > dma_offset)
3018c2ecf20Sopenharmony_ci					*dma_handle = sg_dma_address(&chunk->mem[i]) +
3028c2ecf20Sopenharmony_ci						dma_offset;
3038c2ecf20Sopenharmony_ci				dma_offset -= sg_dma_len(&chunk->mem[i]);
3048c2ecf20Sopenharmony_ci			}
3058c2ecf20Sopenharmony_ci			/* DMA mapping can merge pages but not split them,
3068c2ecf20Sopenharmony_ci			 * so if we found the page, dma_handle has already
3078c2ecf20Sopenharmony_ci			 * been assigned to. */
3088c2ecf20Sopenharmony_ci			if (chunk->mem[i].length > offset) {
3098c2ecf20Sopenharmony_ci				page = sg_page(&chunk->mem[i]);
3108c2ecf20Sopenharmony_ci				goto out;
3118c2ecf20Sopenharmony_ci			}
3128c2ecf20Sopenharmony_ci			offset -= chunk->mem[i].length;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ciout:
3178c2ecf20Sopenharmony_ci	mutex_unlock(&table->mutex);
3188c2ecf20Sopenharmony_ci	return page ? lowmem_page_address(page) + offset : NULL;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ciint mthca_table_get_range(struct mthca_dev *dev, struct mthca_icm_table *table,
3228c2ecf20Sopenharmony_ci			  int start, int end)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int inc = MTHCA_TABLE_CHUNK_SIZE / table->obj_size;
3258c2ecf20Sopenharmony_ci	int i, err;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = start; i <= end; i += inc) {
3288c2ecf20Sopenharmony_ci		err = mthca_table_get(dev, table, i);
3298c2ecf20Sopenharmony_ci		if (err)
3308c2ecf20Sopenharmony_ci			goto fail;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cifail:
3368c2ecf20Sopenharmony_ci	while (i > start) {
3378c2ecf20Sopenharmony_ci		i -= inc;
3388c2ecf20Sopenharmony_ci		mthca_table_put(dev, table, i);
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return err;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_civoid mthca_table_put_range(struct mthca_dev *dev, struct mthca_icm_table *table,
3458c2ecf20Sopenharmony_ci			   int start, int end)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	int i;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
3508c2ecf20Sopenharmony_ci		return;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	for (i = start; i <= end; i += MTHCA_TABLE_CHUNK_SIZE / table->obj_size)
3538c2ecf20Sopenharmony_ci		mthca_table_put(dev, table, i);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistruct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev,
3578c2ecf20Sopenharmony_ci					      u64 virt, int obj_size,
3588c2ecf20Sopenharmony_ci					      int nobj, int reserved,
3598c2ecf20Sopenharmony_ci					      int use_lowmem, int use_coherent)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct mthca_icm_table *table;
3628c2ecf20Sopenharmony_ci	int obj_per_chunk;
3638c2ecf20Sopenharmony_ci	int num_icm;
3648c2ecf20Sopenharmony_ci	unsigned chunk_size;
3658c2ecf20Sopenharmony_ci	int i;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	obj_per_chunk = MTHCA_TABLE_CHUNK_SIZE / obj_size;
3688c2ecf20Sopenharmony_ci	num_icm = DIV_ROUND_UP(nobj, obj_per_chunk);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	table = kmalloc(struct_size(table, icm, num_icm), GFP_KERNEL);
3718c2ecf20Sopenharmony_ci	if (!table)
3728c2ecf20Sopenharmony_ci		return NULL;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	table->virt     = virt;
3758c2ecf20Sopenharmony_ci	table->num_icm  = num_icm;
3768c2ecf20Sopenharmony_ci	table->num_obj  = nobj;
3778c2ecf20Sopenharmony_ci	table->obj_size = obj_size;
3788c2ecf20Sopenharmony_ci	table->lowmem   = use_lowmem;
3798c2ecf20Sopenharmony_ci	table->coherent = use_coherent;
3808c2ecf20Sopenharmony_ci	mutex_init(&table->mutex);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	for (i = 0; i < num_icm; ++i)
3838c2ecf20Sopenharmony_ci		table->icm[i] = NULL;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	for (i = 0; i * MTHCA_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) {
3868c2ecf20Sopenharmony_ci		chunk_size = MTHCA_TABLE_CHUNK_SIZE;
3878c2ecf20Sopenharmony_ci		if ((i + 1) * MTHCA_TABLE_CHUNK_SIZE > nobj * obj_size)
3888c2ecf20Sopenharmony_ci			chunk_size = nobj * obj_size - i * MTHCA_TABLE_CHUNK_SIZE;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		table->icm[i] = mthca_alloc_icm(dev, chunk_size >> PAGE_SHIFT,
3918c2ecf20Sopenharmony_ci						(use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
3928c2ecf20Sopenharmony_ci						__GFP_NOWARN, use_coherent);
3938c2ecf20Sopenharmony_ci		if (!table->icm[i])
3948c2ecf20Sopenharmony_ci			goto err;
3958c2ecf20Sopenharmony_ci		if (mthca_MAP_ICM(dev, table->icm[i],
3968c2ecf20Sopenharmony_ci				  virt + i * MTHCA_TABLE_CHUNK_SIZE)) {
3978c2ecf20Sopenharmony_ci			mthca_free_icm(dev, table->icm[i], table->coherent);
3988c2ecf20Sopenharmony_ci			table->icm[i] = NULL;
3998c2ecf20Sopenharmony_ci			goto err;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		/*
4038c2ecf20Sopenharmony_ci		 * Add a reference to this ICM chunk so that it never
4048c2ecf20Sopenharmony_ci		 * gets freed (since it contains reserved firmware objects).
4058c2ecf20Sopenharmony_ci		 */
4068c2ecf20Sopenharmony_ci		++table->icm[i]->refcount;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return table;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cierr:
4128c2ecf20Sopenharmony_ci	for (i = 0; i < num_icm; ++i)
4138c2ecf20Sopenharmony_ci		if (table->icm[i]) {
4148c2ecf20Sopenharmony_ci			mthca_UNMAP_ICM(dev, virt + i * MTHCA_TABLE_CHUNK_SIZE,
4158c2ecf20Sopenharmony_ci					MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
4168c2ecf20Sopenharmony_ci			mthca_free_icm(dev, table->icm[i], table->coherent);
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	kfree(table);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return NULL;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_civoid mthca_free_icm_table(struct mthca_dev *dev, struct mthca_icm_table *table)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	int i;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	for (i = 0; i < table->num_icm; ++i)
4298c2ecf20Sopenharmony_ci		if (table->icm[i]) {
4308c2ecf20Sopenharmony_ci			mthca_UNMAP_ICM(dev,
4318c2ecf20Sopenharmony_ci					table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
4328c2ecf20Sopenharmony_ci					MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE);
4338c2ecf20Sopenharmony_ci			mthca_free_icm(dev, table->icm[i], table->coherent);
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	kfree(table);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic u64 mthca_uarc_virt(struct mthca_dev *dev, struct mthca_uar *uar, int page)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	return dev->uar_table.uarc_base +
4428c2ecf20Sopenharmony_ci		uar->index * dev->uar_table.uarc_size +
4438c2ecf20Sopenharmony_ci		page * MTHCA_ICM_PAGE_SIZE;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciint mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
4478c2ecf20Sopenharmony_ci		      struct mthca_user_db_table *db_tab, int index, u64 uaddr)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct page *pages[1];
4508c2ecf20Sopenharmony_ci	int ret = 0;
4518c2ecf20Sopenharmony_ci	int i;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
4548c2ecf20Sopenharmony_ci		return 0;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	if (index < 0 || index > dev->uar_table.uarc_size / 8)
4578c2ecf20Sopenharmony_ci		return -EINVAL;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	mutex_lock(&db_tab->mutex);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	i = index / MTHCA_DB_REC_PER_PAGE;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	if ((db_tab->page[i].refcount >= MTHCA_DB_REC_PER_PAGE)       ||
4648c2ecf20Sopenharmony_ci	    (db_tab->page[i].uvirt && db_tab->page[i].uvirt != uaddr) ||
4658c2ecf20Sopenharmony_ci	    (uaddr & 4095)) {
4668c2ecf20Sopenharmony_ci		ret = -EINVAL;
4678c2ecf20Sopenharmony_ci		goto out;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (db_tab->page[i].refcount) {
4718c2ecf20Sopenharmony_ci		++db_tab->page[i].refcount;
4728c2ecf20Sopenharmony_ci		goto out;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	ret = pin_user_pages_fast(uaddr & PAGE_MASK, 1,
4768c2ecf20Sopenharmony_ci				  FOLL_WRITE | FOLL_LONGTERM, pages);
4778c2ecf20Sopenharmony_ci	if (ret < 0)
4788c2ecf20Sopenharmony_ci		goto out;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	sg_set_page(&db_tab->page[i].mem, pages[0], MTHCA_ICM_PAGE_SIZE,
4818c2ecf20Sopenharmony_ci			uaddr & ~PAGE_MASK);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ret = pci_map_sg(dev->pdev, &db_tab->page[i].mem, 1, PCI_DMA_TODEVICE);
4848c2ecf20Sopenharmony_ci	if (ret < 0) {
4858c2ecf20Sopenharmony_ci		unpin_user_page(pages[0]);
4868c2ecf20Sopenharmony_ci		goto out;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	ret = mthca_MAP_ICM_page(dev, sg_dma_address(&db_tab->page[i].mem),
4908c2ecf20Sopenharmony_ci				 mthca_uarc_virt(dev, uar, i));
4918c2ecf20Sopenharmony_ci	if (ret) {
4928c2ecf20Sopenharmony_ci		pci_unmap_sg(dev->pdev, &db_tab->page[i].mem, 1, PCI_DMA_TODEVICE);
4938c2ecf20Sopenharmony_ci		unpin_user_page(sg_page(&db_tab->page[i].mem));
4948c2ecf20Sopenharmony_ci		goto out;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	db_tab->page[i].uvirt    = uaddr;
4988c2ecf20Sopenharmony_ci	db_tab->page[i].refcount = 1;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ciout:
5018c2ecf20Sopenharmony_ci	mutex_unlock(&db_tab->mutex);
5028c2ecf20Sopenharmony_ci	return ret;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_civoid mthca_unmap_user_db(struct mthca_dev *dev, struct mthca_uar *uar,
5068c2ecf20Sopenharmony_ci			 struct mthca_user_db_table *db_tab, int index)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
5098c2ecf20Sopenharmony_ci		return;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/*
5128c2ecf20Sopenharmony_ci	 * To make our bookkeeping simpler, we don't unmap DB
5138c2ecf20Sopenharmony_ci	 * pages until we clean up the whole db table.
5148c2ecf20Sopenharmony_ci	 */
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	mutex_lock(&db_tab->mutex);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	--db_tab->page[index / MTHCA_DB_REC_PER_PAGE].refcount;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	mutex_unlock(&db_tab->mutex);
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistruct mthca_user_db_table *mthca_init_user_db_tab(struct mthca_dev *dev)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct mthca_user_db_table *db_tab;
5268c2ecf20Sopenharmony_ci	int npages;
5278c2ecf20Sopenharmony_ci	int i;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
5308c2ecf20Sopenharmony_ci		return NULL;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	npages = dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE;
5338c2ecf20Sopenharmony_ci	db_tab = kmalloc(struct_size(db_tab, page, npages), GFP_KERNEL);
5348c2ecf20Sopenharmony_ci	if (!db_tab)
5358c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	mutex_init(&db_tab->mutex);
5388c2ecf20Sopenharmony_ci	for (i = 0; i < npages; ++i) {
5398c2ecf20Sopenharmony_ci		db_tab->page[i].refcount = 0;
5408c2ecf20Sopenharmony_ci		db_tab->page[i].uvirt    = 0;
5418c2ecf20Sopenharmony_ci		sg_init_table(&db_tab->page[i].mem, 1);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return db_tab;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_civoid mthca_cleanup_user_db_tab(struct mthca_dev *dev, struct mthca_uar *uar,
5488c2ecf20Sopenharmony_ci			       struct mthca_user_db_table *db_tab)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	int i;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
5538c2ecf20Sopenharmony_ci		return;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	for (i = 0; i < dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE; ++i) {
5568c2ecf20Sopenharmony_ci		if (db_tab->page[i].uvirt) {
5578c2ecf20Sopenharmony_ci			mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, uar, i), 1);
5588c2ecf20Sopenharmony_ci			pci_unmap_sg(dev->pdev, &db_tab->page[i].mem, 1, PCI_DMA_TODEVICE);
5598c2ecf20Sopenharmony_ci			unpin_user_page(sg_page(&db_tab->page[i].mem));
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	kfree(db_tab);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ciint mthca_alloc_db(struct mthca_dev *dev, enum mthca_db_type type,
5678c2ecf20Sopenharmony_ci		   u32 qn, __be32 **db)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	int group;
5708c2ecf20Sopenharmony_ci	int start, end, dir;
5718c2ecf20Sopenharmony_ci	int i, j;
5728c2ecf20Sopenharmony_ci	struct mthca_db_page *page;
5738c2ecf20Sopenharmony_ci	int ret = 0;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	mutex_lock(&dev->db_tab->mutex);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	switch (type) {
5788c2ecf20Sopenharmony_ci	case MTHCA_DB_TYPE_CQ_ARM:
5798c2ecf20Sopenharmony_ci	case MTHCA_DB_TYPE_SQ:
5808c2ecf20Sopenharmony_ci		group = 0;
5818c2ecf20Sopenharmony_ci		start = 0;
5828c2ecf20Sopenharmony_ci		end   = dev->db_tab->max_group1;
5838c2ecf20Sopenharmony_ci		dir   = 1;
5848c2ecf20Sopenharmony_ci		break;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	case MTHCA_DB_TYPE_CQ_SET_CI:
5878c2ecf20Sopenharmony_ci	case MTHCA_DB_TYPE_RQ:
5888c2ecf20Sopenharmony_ci	case MTHCA_DB_TYPE_SRQ:
5898c2ecf20Sopenharmony_ci		group = 1;
5908c2ecf20Sopenharmony_ci		start = dev->db_tab->npages - 1;
5918c2ecf20Sopenharmony_ci		end   = dev->db_tab->min_group2;
5928c2ecf20Sopenharmony_ci		dir   = -1;
5938c2ecf20Sopenharmony_ci		break;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	default:
5968c2ecf20Sopenharmony_ci		ret = -EINVAL;
5978c2ecf20Sopenharmony_ci		goto out;
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	for (i = start; i != end; i += dir)
6018c2ecf20Sopenharmony_ci		if (dev->db_tab->page[i].db_rec &&
6028c2ecf20Sopenharmony_ci		    !bitmap_full(dev->db_tab->page[i].used,
6038c2ecf20Sopenharmony_ci				 MTHCA_DB_REC_PER_PAGE)) {
6048c2ecf20Sopenharmony_ci			page = dev->db_tab->page + i;
6058c2ecf20Sopenharmony_ci			goto found;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	for (i = start; i != end; i += dir)
6098c2ecf20Sopenharmony_ci		if (!dev->db_tab->page[i].db_rec) {
6108c2ecf20Sopenharmony_ci			page = dev->db_tab->page + i;
6118c2ecf20Sopenharmony_ci			goto alloc;
6128c2ecf20Sopenharmony_ci		}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (dev->db_tab->max_group1 >= dev->db_tab->min_group2 - 1) {
6158c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6168c2ecf20Sopenharmony_ci		goto out;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (group == 0)
6208c2ecf20Sopenharmony_ci		++dev->db_tab->max_group1;
6218c2ecf20Sopenharmony_ci	else
6228c2ecf20Sopenharmony_ci		--dev->db_tab->min_group2;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	page = dev->db_tab->page + end;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cialloc:
6278c2ecf20Sopenharmony_ci	page->db_rec = dma_alloc_coherent(&dev->pdev->dev,
6288c2ecf20Sopenharmony_ci					  MTHCA_ICM_PAGE_SIZE, &page->mapping,
6298c2ecf20Sopenharmony_ci					  GFP_KERNEL);
6308c2ecf20Sopenharmony_ci	if (!page->db_rec) {
6318c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6328c2ecf20Sopenharmony_ci		goto out;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	ret = mthca_MAP_ICM_page(dev, page->mapping,
6368c2ecf20Sopenharmony_ci				 mthca_uarc_virt(dev, &dev->driver_uar, i));
6378c2ecf20Sopenharmony_ci	if (ret) {
6388c2ecf20Sopenharmony_ci		dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
6398c2ecf20Sopenharmony_ci				  page->db_rec, page->mapping);
6408c2ecf20Sopenharmony_ci		goto out;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	bitmap_zero(page->used, MTHCA_DB_REC_PER_PAGE);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cifound:
6468c2ecf20Sopenharmony_ci	j = find_first_zero_bit(page->used, MTHCA_DB_REC_PER_PAGE);
6478c2ecf20Sopenharmony_ci	set_bit(j, page->used);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (group == 1)
6508c2ecf20Sopenharmony_ci		j = MTHCA_DB_REC_PER_PAGE - 1 - j;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	ret = i * MTHCA_DB_REC_PER_PAGE + j;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	page->db_rec[j] = cpu_to_be64((qn << 8) | (type << 5));
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	*db = (__be32 *) &page->db_rec[j];
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ciout:
6598c2ecf20Sopenharmony_ci	mutex_unlock(&dev->db_tab->mutex);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return ret;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_civoid mthca_free_db(struct mthca_dev *dev, int type, int db_index)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	int i, j;
6678c2ecf20Sopenharmony_ci	struct mthca_db_page *page;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	i = db_index / MTHCA_DB_REC_PER_PAGE;
6708c2ecf20Sopenharmony_ci	j = db_index % MTHCA_DB_REC_PER_PAGE;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	page = dev->db_tab->page + i;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	mutex_lock(&dev->db_tab->mutex);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	page->db_rec[j] = 0;
6778c2ecf20Sopenharmony_ci	if (i >= dev->db_tab->min_group2)
6788c2ecf20Sopenharmony_ci		j = MTHCA_DB_REC_PER_PAGE - 1 - j;
6798c2ecf20Sopenharmony_ci	clear_bit(j, page->used);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (bitmap_empty(page->used, MTHCA_DB_REC_PER_PAGE) &&
6828c2ecf20Sopenharmony_ci	    i >= dev->db_tab->max_group1 - 1) {
6838c2ecf20Sopenharmony_ci		mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
6868c2ecf20Sopenharmony_ci				  page->db_rec, page->mapping);
6878c2ecf20Sopenharmony_ci		page->db_rec = NULL;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci		if (i == dev->db_tab->max_group1) {
6908c2ecf20Sopenharmony_ci			--dev->db_tab->max_group1;
6918c2ecf20Sopenharmony_ci			/* XXX may be able to unmap more pages now */
6928c2ecf20Sopenharmony_ci		}
6938c2ecf20Sopenharmony_ci		if (i == dev->db_tab->min_group2)
6948c2ecf20Sopenharmony_ci			++dev->db_tab->min_group2;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	mutex_unlock(&dev->db_tab->mutex);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ciint mthca_init_db_tab(struct mthca_dev *dev)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	int i;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
7058c2ecf20Sopenharmony_ci		return 0;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	dev->db_tab = kmalloc(sizeof *dev->db_tab, GFP_KERNEL);
7088c2ecf20Sopenharmony_ci	if (!dev->db_tab)
7098c2ecf20Sopenharmony_ci		return -ENOMEM;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	mutex_init(&dev->db_tab->mutex);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	dev->db_tab->npages     = dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE;
7148c2ecf20Sopenharmony_ci	dev->db_tab->max_group1 = 0;
7158c2ecf20Sopenharmony_ci	dev->db_tab->min_group2 = dev->db_tab->npages - 1;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	dev->db_tab->page = kmalloc_array(dev->db_tab->npages,
7188c2ecf20Sopenharmony_ci					  sizeof(*dev->db_tab->page),
7198c2ecf20Sopenharmony_ci					  GFP_KERNEL);
7208c2ecf20Sopenharmony_ci	if (!dev->db_tab->page) {
7218c2ecf20Sopenharmony_ci		kfree(dev->db_tab);
7228c2ecf20Sopenharmony_ci		return -ENOMEM;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	for (i = 0; i < dev->db_tab->npages; ++i)
7268c2ecf20Sopenharmony_ci		dev->db_tab->page[i].db_rec = NULL;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return 0;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_civoid mthca_cleanup_db_tab(struct mthca_dev *dev)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	int i;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev))
7368c2ecf20Sopenharmony_ci		return;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/*
7398c2ecf20Sopenharmony_ci	 * Because we don't always free our UARC pages when they
7408c2ecf20Sopenharmony_ci	 * become empty to make mthca_free_db() simpler we need to
7418c2ecf20Sopenharmony_ci	 * make a sweep through the doorbell pages and free any
7428c2ecf20Sopenharmony_ci	 * leftover pages now.
7438c2ecf20Sopenharmony_ci	 */
7448c2ecf20Sopenharmony_ci	for (i = 0; i < dev->db_tab->npages; ++i) {
7458c2ecf20Sopenharmony_ci		if (!dev->db_tab->page[i].db_rec)
7468c2ecf20Sopenharmony_ci			continue;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		if (!bitmap_empty(dev->db_tab->page[i].used, MTHCA_DB_REC_PER_PAGE))
7498c2ecf20Sopenharmony_ci			mthca_warn(dev, "Kernel UARC page %d not empty\n", i);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci		dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE,
7548c2ecf20Sopenharmony_ci				  dev->db_tab->page[i].db_rec,
7558c2ecf20Sopenharmony_ci				  dev->db_tab->page[i].mapping);
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	kfree(dev->db_tab->page);
7598c2ecf20Sopenharmony_ci	kfree(dev->db_tab);
7608c2ecf20Sopenharmony_ci}
761