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