162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2005 Cisco Systems. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is available to you under a choice of one of two 762306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1062306a36Sopenharmony_ci * OpenIB.org BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1362306a36Sopenharmony_ci * without modification, are permitted provided that the following 1462306a36Sopenharmony_ci * conditions are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1762306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1862306a36Sopenharmony_ci * disclaimer. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2162306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2262306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2362306a36Sopenharmony_ci * provided with the distribution. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3262306a36Sopenharmony_ci * SOFTWARE. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/mm.h> 3662306a36Sopenharmony_ci#include <linux/scatterlist.h> 3762306a36Sopenharmony_ci#include <linux/sched.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <asm/page.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "mthca_memfree.h" 4362306a36Sopenharmony_ci#include "mthca_dev.h" 4462306a36Sopenharmony_ci#include "mthca_cmd.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * We allocate in as big chunks as we can, up to a maximum of 256 KB 4862306a36Sopenharmony_ci * per chunk. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cienum { 5162306a36Sopenharmony_ci MTHCA_ICM_ALLOC_SIZE = 1 << 18, 5262306a36Sopenharmony_ci MTHCA_TABLE_CHUNK_SIZE = 1 << 18 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct mthca_user_db_table { 5662306a36Sopenharmony_ci struct mutex mutex; 5762306a36Sopenharmony_ci struct { 5862306a36Sopenharmony_ci u64 uvirt; 5962306a36Sopenharmony_ci struct scatterlist mem; 6062306a36Sopenharmony_ci int refcount; 6162306a36Sopenharmony_ci } page[]; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void mthca_free_icm_pages(struct mthca_dev *dev, struct mthca_icm_chunk *chunk) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci int i; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (chunk->nsg > 0) 6962306a36Sopenharmony_ci dma_unmap_sg(&dev->pdev->dev, chunk->mem, chunk->npages, 7062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 7362306a36Sopenharmony_ci __free_pages(sg_page(&chunk->mem[i]), 7462306a36Sopenharmony_ci get_order(chunk->mem[i].length)); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void mthca_free_icm_coherent(struct mthca_dev *dev, struct mthca_icm_chunk *chunk) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int i; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) { 8262306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, chunk->mem[i].length, 8362306a36Sopenharmony_ci lowmem_page_address(sg_page(&chunk->mem[i])), 8462306a36Sopenharmony_ci sg_dma_address(&chunk->mem[i])); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_civoid mthca_free_icm(struct mthca_dev *dev, struct mthca_icm *icm, int coherent) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct mthca_icm_chunk *chunk, *tmp; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!icm) 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) { 9662306a36Sopenharmony_ci if (coherent) 9762306a36Sopenharmony_ci mthca_free_icm_coherent(dev, chunk); 9862306a36Sopenharmony_ci else 9962306a36Sopenharmony_ci mthca_free_icm_pages(dev, chunk); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci kfree(chunk); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci kfree(icm); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int mthca_alloc_icm_pages(struct scatterlist *mem, int order, gfp_t gfp_mask) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct page *page; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Use __GFP_ZERO because buggy firmware assumes ICM pages are 11362306a36Sopenharmony_ci * cleared, and subtle failures are seen if they aren't. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci page = alloc_pages(gfp_mask | __GFP_ZERO, order); 11662306a36Sopenharmony_ci if (!page) 11762306a36Sopenharmony_ci return -ENOMEM; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci sg_set_page(mem, page, PAGE_SIZE << order, 0); 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int mthca_alloc_icm_coherent(struct device *dev, struct scatterlist *mem, 12462306a36Sopenharmony_ci int order, gfp_t gfp_mask) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci void *buf = dma_alloc_coherent(dev, PAGE_SIZE << order, &sg_dma_address(mem), 12762306a36Sopenharmony_ci gfp_mask); 12862306a36Sopenharmony_ci if (!buf) 12962306a36Sopenharmony_ci return -ENOMEM; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci sg_set_buf(mem, buf, PAGE_SIZE << order); 13262306a36Sopenharmony_ci BUG_ON(mem->offset); 13362306a36Sopenharmony_ci sg_dma_len(mem) = PAGE_SIZE << order; 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistruct mthca_icm *mthca_alloc_icm(struct mthca_dev *dev, int npages, 13862306a36Sopenharmony_ci gfp_t gfp_mask, int coherent) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct mthca_icm *icm; 14162306a36Sopenharmony_ci struct mthca_icm_chunk *chunk = NULL; 14262306a36Sopenharmony_ci int cur_order; 14362306a36Sopenharmony_ci int ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* We use sg_set_buf for coherent allocs, which assumes low memory */ 14662306a36Sopenharmony_ci BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 14962306a36Sopenharmony_ci if (!icm) 15062306a36Sopenharmony_ci return icm; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci icm->refcount = 0; 15362306a36Sopenharmony_ci INIT_LIST_HEAD(&icm->chunk_list); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci cur_order = get_order(MTHCA_ICM_ALLOC_SIZE); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci while (npages > 0) { 15862306a36Sopenharmony_ci if (!chunk) { 15962306a36Sopenharmony_ci chunk = kmalloc(sizeof *chunk, 16062306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 16162306a36Sopenharmony_ci if (!chunk) 16262306a36Sopenharmony_ci goto fail; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci sg_init_table(chunk->mem, MTHCA_ICM_CHUNK_LEN); 16562306a36Sopenharmony_ci chunk->npages = 0; 16662306a36Sopenharmony_ci chunk->nsg = 0; 16762306a36Sopenharmony_ci list_add_tail(&chunk->list, &icm->chunk_list); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci while (1 << cur_order > npages) 17162306a36Sopenharmony_ci --cur_order; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (coherent) 17462306a36Sopenharmony_ci ret = mthca_alloc_icm_coherent(&dev->pdev->dev, 17562306a36Sopenharmony_ci &chunk->mem[chunk->npages], 17662306a36Sopenharmony_ci cur_order, gfp_mask); 17762306a36Sopenharmony_ci else 17862306a36Sopenharmony_ci ret = mthca_alloc_icm_pages(&chunk->mem[chunk->npages], 17962306a36Sopenharmony_ci cur_order, gfp_mask); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!ret) { 18262306a36Sopenharmony_ci ++chunk->npages; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (coherent) 18562306a36Sopenharmony_ci ++chunk->nsg; 18662306a36Sopenharmony_ci else if (chunk->npages == MTHCA_ICM_CHUNK_LEN) { 18762306a36Sopenharmony_ci chunk->nsg = 18862306a36Sopenharmony_ci dma_map_sg(&dev->pdev->dev, chunk->mem, 18962306a36Sopenharmony_ci chunk->npages, 19062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (chunk->nsg <= 0) 19362306a36Sopenharmony_ci goto fail; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (chunk->npages == MTHCA_ICM_CHUNK_LEN) 19762306a36Sopenharmony_ci chunk = NULL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci npages -= 1 << cur_order; 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci --cur_order; 20262306a36Sopenharmony_ci if (cur_order < 0) 20362306a36Sopenharmony_ci goto fail; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!coherent && chunk) { 20862306a36Sopenharmony_ci chunk->nsg = dma_map_sg(&dev->pdev->dev, chunk->mem, 20962306a36Sopenharmony_ci chunk->npages, DMA_BIDIRECTIONAL); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (chunk->nsg <= 0) 21262306a36Sopenharmony_ci goto fail; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return icm; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cifail: 21862306a36Sopenharmony_ci mthca_free_icm(dev, icm, coherent); 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciint mthca_table_get(struct mthca_dev *dev, struct mthca_icm_table *table, int obj) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE; 22562306a36Sopenharmony_ci int ret = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci mutex_lock(&table->mutex); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (table->icm[i]) { 23062306a36Sopenharmony_ci ++table->icm[i]->refcount; 23162306a36Sopenharmony_ci goto out; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci table->icm[i] = mthca_alloc_icm(dev, MTHCA_TABLE_CHUNK_SIZE >> PAGE_SHIFT, 23562306a36Sopenharmony_ci (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 23662306a36Sopenharmony_ci __GFP_NOWARN, table->coherent); 23762306a36Sopenharmony_ci if (!table->icm[i]) { 23862306a36Sopenharmony_ci ret = -ENOMEM; 23962306a36Sopenharmony_ci goto out; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (mthca_MAP_ICM(dev, table->icm[i], 24362306a36Sopenharmony_ci table->virt + i * MTHCA_TABLE_CHUNK_SIZE)) { 24462306a36Sopenharmony_ci mthca_free_icm(dev, table->icm[i], table->coherent); 24562306a36Sopenharmony_ci table->icm[i] = NULL; 24662306a36Sopenharmony_ci ret = -ENOMEM; 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci ++table->icm[i]->refcount; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciout: 25362306a36Sopenharmony_ci mutex_unlock(&table->mutex); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_civoid mthca_table_put(struct mthca_dev *dev, struct mthca_icm_table *table, int obj) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int i; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci i = (obj & (table->num_obj - 1)) * table->obj_size / MTHCA_TABLE_CHUNK_SIZE; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci mutex_lock(&table->mutex); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (--table->icm[i]->refcount == 0) { 26962306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, table->virt + i * MTHCA_TABLE_CHUNK_SIZE, 27062306a36Sopenharmony_ci MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE); 27162306a36Sopenharmony_ci mthca_free_icm(dev, table->icm[i], table->coherent); 27262306a36Sopenharmony_ci table->icm[i] = NULL; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci mutex_unlock(&table->mutex); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_civoid *mthca_table_find(struct mthca_icm_table *table, int obj, dma_addr_t *dma_handle) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci int idx, offset, dma_offset, i; 28162306a36Sopenharmony_ci struct mthca_icm_chunk *chunk; 28262306a36Sopenharmony_ci struct mthca_icm *icm; 28362306a36Sopenharmony_ci struct page *page = NULL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!table->lowmem) 28662306a36Sopenharmony_ci return NULL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mutex_lock(&table->mutex); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci idx = (obj & (table->num_obj - 1)) * table->obj_size; 29162306a36Sopenharmony_ci icm = table->icm[idx / MTHCA_TABLE_CHUNK_SIZE]; 29262306a36Sopenharmony_ci dma_offset = offset = idx % MTHCA_TABLE_CHUNK_SIZE; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!icm) 29562306a36Sopenharmony_ci goto out; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci list_for_each_entry(chunk, &icm->chunk_list, list) { 29862306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) { 29962306a36Sopenharmony_ci if (dma_handle && dma_offset >= 0) { 30062306a36Sopenharmony_ci if (sg_dma_len(&chunk->mem[i]) > dma_offset) 30162306a36Sopenharmony_ci *dma_handle = sg_dma_address(&chunk->mem[i]) + 30262306a36Sopenharmony_ci dma_offset; 30362306a36Sopenharmony_ci dma_offset -= sg_dma_len(&chunk->mem[i]); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci /* DMA mapping can merge pages but not split them, 30662306a36Sopenharmony_ci * so if we found the page, dma_handle has already 30762306a36Sopenharmony_ci * been assigned to. */ 30862306a36Sopenharmony_ci if (chunk->mem[i].length > offset) { 30962306a36Sopenharmony_ci page = sg_page(&chunk->mem[i]); 31062306a36Sopenharmony_ci goto out; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci offset -= chunk->mem[i].length; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci mutex_unlock(&table->mutex); 31862306a36Sopenharmony_ci return page ? lowmem_page_address(page) + offset : NULL; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint mthca_table_get_range(struct mthca_dev *dev, struct mthca_icm_table *table, 32262306a36Sopenharmony_ci int start, int end) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int inc = MTHCA_TABLE_CHUNK_SIZE / table->obj_size; 32562306a36Sopenharmony_ci int i, err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = start; i <= end; i += inc) { 32862306a36Sopenharmony_ci err = mthca_table_get(dev, table, i); 32962306a36Sopenharmony_ci if (err) 33062306a36Sopenharmony_ci goto fail; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cifail: 33662306a36Sopenharmony_ci while (i > start) { 33762306a36Sopenharmony_ci i -= inc; 33862306a36Sopenharmony_ci mthca_table_put(dev, table, i); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return err; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_civoid mthca_table_put_range(struct mthca_dev *dev, struct mthca_icm_table *table, 34562306a36Sopenharmony_ci int start, int end) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int i; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci for (i = start; i <= end; i += MTHCA_TABLE_CHUNK_SIZE / table->obj_size) 35362306a36Sopenharmony_ci mthca_table_put(dev, table, i); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistruct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev, 35762306a36Sopenharmony_ci u64 virt, int obj_size, 35862306a36Sopenharmony_ci int nobj, int reserved, 35962306a36Sopenharmony_ci int use_lowmem, int use_coherent) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct mthca_icm_table *table; 36262306a36Sopenharmony_ci int obj_per_chunk; 36362306a36Sopenharmony_ci int num_icm; 36462306a36Sopenharmony_ci unsigned chunk_size; 36562306a36Sopenharmony_ci int i; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci obj_per_chunk = MTHCA_TABLE_CHUNK_SIZE / obj_size; 36862306a36Sopenharmony_ci num_icm = DIV_ROUND_UP(nobj, obj_per_chunk); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci table = kmalloc(struct_size(table, icm, num_icm), GFP_KERNEL); 37162306a36Sopenharmony_ci if (!table) 37262306a36Sopenharmony_ci return NULL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci table->virt = virt; 37562306a36Sopenharmony_ci table->num_icm = num_icm; 37662306a36Sopenharmony_ci table->num_obj = nobj; 37762306a36Sopenharmony_ci table->obj_size = obj_size; 37862306a36Sopenharmony_ci table->lowmem = use_lowmem; 37962306a36Sopenharmony_ci table->coherent = use_coherent; 38062306a36Sopenharmony_ci mutex_init(&table->mutex); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < num_icm; ++i) 38362306a36Sopenharmony_ci table->icm[i] = NULL; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (i = 0; i * MTHCA_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) { 38662306a36Sopenharmony_ci chunk_size = MTHCA_TABLE_CHUNK_SIZE; 38762306a36Sopenharmony_ci if ((i + 1) * MTHCA_TABLE_CHUNK_SIZE > nobj * obj_size) 38862306a36Sopenharmony_ci chunk_size = nobj * obj_size - i * MTHCA_TABLE_CHUNK_SIZE; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci table->icm[i] = mthca_alloc_icm(dev, chunk_size >> PAGE_SHIFT, 39162306a36Sopenharmony_ci (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 39262306a36Sopenharmony_ci __GFP_NOWARN, use_coherent); 39362306a36Sopenharmony_ci if (!table->icm[i]) 39462306a36Sopenharmony_ci goto err; 39562306a36Sopenharmony_ci if (mthca_MAP_ICM(dev, table->icm[i], 39662306a36Sopenharmony_ci virt + i * MTHCA_TABLE_CHUNK_SIZE)) { 39762306a36Sopenharmony_ci mthca_free_icm(dev, table->icm[i], table->coherent); 39862306a36Sopenharmony_ci table->icm[i] = NULL; 39962306a36Sopenharmony_ci goto err; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * Add a reference to this ICM chunk so that it never 40462306a36Sopenharmony_ci * gets freed (since it contains reserved firmware objects). 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci ++table->icm[i]->refcount; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return table; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cierr: 41262306a36Sopenharmony_ci for (i = 0; i < num_icm; ++i) 41362306a36Sopenharmony_ci if (table->icm[i]) { 41462306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, virt + i * MTHCA_TABLE_CHUNK_SIZE, 41562306a36Sopenharmony_ci MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE); 41662306a36Sopenharmony_ci mthca_free_icm(dev, table->icm[i], table->coherent); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci kfree(table); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_civoid mthca_free_icm_table(struct mthca_dev *dev, struct mthca_icm_table *table) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int i; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci for (i = 0; i < table->num_icm; ++i) 42962306a36Sopenharmony_ci if (table->icm[i]) { 43062306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, 43162306a36Sopenharmony_ci table->virt + i * MTHCA_TABLE_CHUNK_SIZE, 43262306a36Sopenharmony_ci MTHCA_TABLE_CHUNK_SIZE / MTHCA_ICM_PAGE_SIZE); 43362306a36Sopenharmony_ci mthca_free_icm(dev, table->icm[i], table->coherent); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci kfree(table); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic u64 mthca_uarc_virt(struct mthca_dev *dev, struct mthca_uar *uar, int page) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci return dev->uar_table.uarc_base + 44262306a36Sopenharmony_ci uar->index * dev->uar_table.uarc_size + 44362306a36Sopenharmony_ci page * MTHCA_ICM_PAGE_SIZE; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciint mthca_map_user_db(struct mthca_dev *dev, struct mthca_uar *uar, 44762306a36Sopenharmony_ci struct mthca_user_db_table *db_tab, int index, u64 uaddr) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct page *pages[1]; 45062306a36Sopenharmony_ci int ret = 0; 45162306a36Sopenharmony_ci int i; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (index < 0 || index > dev->uar_table.uarc_size / 8) 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci mutex_lock(&db_tab->mutex); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci i = index / MTHCA_DB_REC_PER_PAGE; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if ((db_tab->page[i].refcount >= MTHCA_DB_REC_PER_PAGE) || 46462306a36Sopenharmony_ci (db_tab->page[i].uvirt && db_tab->page[i].uvirt != uaddr) || 46562306a36Sopenharmony_ci (uaddr & 4095)) { 46662306a36Sopenharmony_ci ret = -EINVAL; 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (db_tab->page[i].refcount) { 47162306a36Sopenharmony_ci ++db_tab->page[i].refcount; 47262306a36Sopenharmony_ci goto out; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = pin_user_pages_fast(uaddr & PAGE_MASK, 1, 47662306a36Sopenharmony_ci FOLL_WRITE | FOLL_LONGTERM, pages); 47762306a36Sopenharmony_ci if (ret < 0) 47862306a36Sopenharmony_ci goto out; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci sg_set_page(&db_tab->page[i].mem, pages[0], MTHCA_ICM_PAGE_SIZE, 48162306a36Sopenharmony_ci uaddr & ~PAGE_MASK); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ret = dma_map_sg(&dev->pdev->dev, &db_tab->page[i].mem, 1, 48462306a36Sopenharmony_ci DMA_TO_DEVICE); 48562306a36Sopenharmony_ci if (ret < 0) { 48662306a36Sopenharmony_ci unpin_user_page(pages[0]); 48762306a36Sopenharmony_ci goto out; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = mthca_MAP_ICM_page(dev, sg_dma_address(&db_tab->page[i].mem), 49162306a36Sopenharmony_ci mthca_uarc_virt(dev, uar, i)); 49262306a36Sopenharmony_ci if (ret) { 49362306a36Sopenharmony_ci dma_unmap_sg(&dev->pdev->dev, &db_tab->page[i].mem, 1, 49462306a36Sopenharmony_ci DMA_TO_DEVICE); 49562306a36Sopenharmony_ci unpin_user_page(sg_page(&db_tab->page[i].mem)); 49662306a36Sopenharmony_ci goto out; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci db_tab->page[i].uvirt = uaddr; 50062306a36Sopenharmony_ci db_tab->page[i].refcount = 1; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ciout: 50362306a36Sopenharmony_ci mutex_unlock(&db_tab->mutex); 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_civoid mthca_unmap_user_db(struct mthca_dev *dev, struct mthca_uar *uar, 50862306a36Sopenharmony_ci struct mthca_user_db_table *db_tab, int index) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * To make our bookkeeping simpler, we don't unmap DB 51562306a36Sopenharmony_ci * pages until we clean up the whole db table. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci mutex_lock(&db_tab->mutex); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci --db_tab->page[index / MTHCA_DB_REC_PER_PAGE].refcount; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci mutex_unlock(&db_tab->mutex); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistruct mthca_user_db_table *mthca_init_user_db_tab(struct mthca_dev *dev) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct mthca_user_db_table *db_tab; 52862306a36Sopenharmony_ci int npages; 52962306a36Sopenharmony_ci int i; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 53262306a36Sopenharmony_ci return NULL; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci npages = dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE; 53562306a36Sopenharmony_ci db_tab = kmalloc(struct_size(db_tab, page, npages), GFP_KERNEL); 53662306a36Sopenharmony_ci if (!db_tab) 53762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci mutex_init(&db_tab->mutex); 54062306a36Sopenharmony_ci for (i = 0; i < npages; ++i) { 54162306a36Sopenharmony_ci db_tab->page[i].refcount = 0; 54262306a36Sopenharmony_ci db_tab->page[i].uvirt = 0; 54362306a36Sopenharmony_ci sg_init_table(&db_tab->page[i].mem, 1); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return db_tab; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_civoid mthca_cleanup_user_db_tab(struct mthca_dev *dev, struct mthca_uar *uar, 55062306a36Sopenharmony_ci struct mthca_user_db_table *db_tab) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci int i; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci for (i = 0; i < dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE; ++i) { 55862306a36Sopenharmony_ci if (db_tab->page[i].uvirt) { 55962306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, uar, i), 1); 56062306a36Sopenharmony_ci dma_unmap_sg(&dev->pdev->dev, &db_tab->page[i].mem, 1, 56162306a36Sopenharmony_ci DMA_TO_DEVICE); 56262306a36Sopenharmony_ci unpin_user_page(sg_page(&db_tab->page[i].mem)); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci kfree(db_tab); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ciint mthca_alloc_db(struct mthca_dev *dev, enum mthca_db_type type, 57062306a36Sopenharmony_ci u32 qn, __be32 **db) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci int group; 57362306a36Sopenharmony_ci int start, end, dir; 57462306a36Sopenharmony_ci int i, j; 57562306a36Sopenharmony_ci struct mthca_db_page *page; 57662306a36Sopenharmony_ci int ret = 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci mutex_lock(&dev->db_tab->mutex); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci switch (type) { 58162306a36Sopenharmony_ci case MTHCA_DB_TYPE_CQ_ARM: 58262306a36Sopenharmony_ci case MTHCA_DB_TYPE_SQ: 58362306a36Sopenharmony_ci group = 0; 58462306a36Sopenharmony_ci start = 0; 58562306a36Sopenharmony_ci end = dev->db_tab->max_group1; 58662306a36Sopenharmony_ci dir = 1; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci case MTHCA_DB_TYPE_CQ_SET_CI: 59062306a36Sopenharmony_ci case MTHCA_DB_TYPE_RQ: 59162306a36Sopenharmony_ci case MTHCA_DB_TYPE_SRQ: 59262306a36Sopenharmony_ci group = 1; 59362306a36Sopenharmony_ci start = dev->db_tab->npages - 1; 59462306a36Sopenharmony_ci end = dev->db_tab->min_group2; 59562306a36Sopenharmony_ci dir = -1; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci default: 59962306a36Sopenharmony_ci ret = -EINVAL; 60062306a36Sopenharmony_ci goto out; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci for (i = start; i != end; i += dir) 60462306a36Sopenharmony_ci if (dev->db_tab->page[i].db_rec && 60562306a36Sopenharmony_ci !bitmap_full(dev->db_tab->page[i].used, 60662306a36Sopenharmony_ci MTHCA_DB_REC_PER_PAGE)) { 60762306a36Sopenharmony_ci page = dev->db_tab->page + i; 60862306a36Sopenharmony_ci goto found; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for (i = start; i != end; i += dir) 61262306a36Sopenharmony_ci if (!dev->db_tab->page[i].db_rec) { 61362306a36Sopenharmony_ci page = dev->db_tab->page + i; 61462306a36Sopenharmony_ci goto alloc; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (dev->db_tab->max_group1 >= dev->db_tab->min_group2 - 1) { 61862306a36Sopenharmony_ci ret = -ENOMEM; 61962306a36Sopenharmony_ci goto out; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (group == 0) 62362306a36Sopenharmony_ci ++dev->db_tab->max_group1; 62462306a36Sopenharmony_ci else 62562306a36Sopenharmony_ci --dev->db_tab->min_group2; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci page = dev->db_tab->page + end; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cialloc: 63062306a36Sopenharmony_ci page->db_rec = dma_alloc_coherent(&dev->pdev->dev, 63162306a36Sopenharmony_ci MTHCA_ICM_PAGE_SIZE, &page->mapping, 63262306a36Sopenharmony_ci GFP_KERNEL); 63362306a36Sopenharmony_ci if (!page->db_rec) { 63462306a36Sopenharmony_ci ret = -ENOMEM; 63562306a36Sopenharmony_ci goto out; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ret = mthca_MAP_ICM_page(dev, page->mapping, 63962306a36Sopenharmony_ci mthca_uarc_virt(dev, &dev->driver_uar, i)); 64062306a36Sopenharmony_ci if (ret) { 64162306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE, 64262306a36Sopenharmony_ci page->db_rec, page->mapping); 64362306a36Sopenharmony_ci goto out; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci bitmap_zero(page->used, MTHCA_DB_REC_PER_PAGE); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cifound: 64962306a36Sopenharmony_ci j = find_first_zero_bit(page->used, MTHCA_DB_REC_PER_PAGE); 65062306a36Sopenharmony_ci set_bit(j, page->used); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (group == 1) 65362306a36Sopenharmony_ci j = MTHCA_DB_REC_PER_PAGE - 1 - j; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ret = i * MTHCA_DB_REC_PER_PAGE + j; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci page->db_rec[j] = cpu_to_be64((qn << 8) | (type << 5)); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci *db = (__be32 *) &page->db_rec[j]; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ciout: 66262306a36Sopenharmony_ci mutex_unlock(&dev->db_tab->mutex); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return ret; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_civoid mthca_free_db(struct mthca_dev *dev, int type, int db_index) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci int i, j; 67062306a36Sopenharmony_ci struct mthca_db_page *page; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci i = db_index / MTHCA_DB_REC_PER_PAGE; 67362306a36Sopenharmony_ci j = db_index % MTHCA_DB_REC_PER_PAGE; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci page = dev->db_tab->page + i; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci mutex_lock(&dev->db_tab->mutex); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci page->db_rec[j] = 0; 68062306a36Sopenharmony_ci if (i >= dev->db_tab->min_group2) 68162306a36Sopenharmony_ci j = MTHCA_DB_REC_PER_PAGE - 1 - j; 68262306a36Sopenharmony_ci clear_bit(j, page->used); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (bitmap_empty(page->used, MTHCA_DB_REC_PER_PAGE) && 68562306a36Sopenharmony_ci i >= dev->db_tab->max_group1 - 1) { 68662306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE, 68962306a36Sopenharmony_ci page->db_rec, page->mapping); 69062306a36Sopenharmony_ci page->db_rec = NULL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (i == dev->db_tab->max_group1) { 69362306a36Sopenharmony_ci --dev->db_tab->max_group1; 69462306a36Sopenharmony_ci /* XXX may be able to unmap more pages now */ 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci if (i == dev->db_tab->min_group2) 69762306a36Sopenharmony_ci ++dev->db_tab->min_group2; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci mutex_unlock(&dev->db_tab->mutex); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ciint mthca_init_db_tab(struct mthca_dev *dev) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci int i; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci dev->db_tab = kmalloc(sizeof *dev->db_tab, GFP_KERNEL); 71162306a36Sopenharmony_ci if (!dev->db_tab) 71262306a36Sopenharmony_ci return -ENOMEM; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mutex_init(&dev->db_tab->mutex); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci dev->db_tab->npages = dev->uar_table.uarc_size / MTHCA_ICM_PAGE_SIZE; 71762306a36Sopenharmony_ci dev->db_tab->max_group1 = 0; 71862306a36Sopenharmony_ci dev->db_tab->min_group2 = dev->db_tab->npages - 1; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev->db_tab->page = kmalloc_array(dev->db_tab->npages, 72162306a36Sopenharmony_ci sizeof(*dev->db_tab->page), 72262306a36Sopenharmony_ci GFP_KERNEL); 72362306a36Sopenharmony_ci if (!dev->db_tab->page) { 72462306a36Sopenharmony_ci kfree(dev->db_tab); 72562306a36Sopenharmony_ci return -ENOMEM; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci for (i = 0; i < dev->db_tab->npages; ++i) 72962306a36Sopenharmony_ci dev->db_tab->page[i].db_rec = NULL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_civoid mthca_cleanup_db_tab(struct mthca_dev *dev) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci int i; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) 73962306a36Sopenharmony_ci return; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* 74262306a36Sopenharmony_ci * Because we don't always free our UARC pages when they 74362306a36Sopenharmony_ci * become empty to make mthca_free_db() simpler we need to 74462306a36Sopenharmony_ci * make a sweep through the doorbell pages and free any 74562306a36Sopenharmony_ci * leftover pages now. 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci for (i = 0; i < dev->db_tab->npages; ++i) { 74862306a36Sopenharmony_ci if (!dev->db_tab->page[i].db_rec) 74962306a36Sopenharmony_ci continue; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (!bitmap_empty(dev->db_tab->page[i].used, MTHCA_DB_REC_PER_PAGE)) 75262306a36Sopenharmony_ci mthca_warn(dev, "Kernel UARC page %d not empty\n", i); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, mthca_uarc_virt(dev, &dev->driver_uar, i), 1); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, MTHCA_ICM_PAGE_SIZE, 75762306a36Sopenharmony_ci dev->db_tab->page[i].db_rec, 75862306a36Sopenharmony_ci dev->db_tab->page[i].mapping); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci kfree(dev->db_tab->page); 76262306a36Sopenharmony_ci kfree(dev->db_tab); 76362306a36Sopenharmony_ci} 764