18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 68c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 128c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 138c2ecf20Sopenharmony_ci * conditions are met: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 168c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 178c2ecf20Sopenharmony_ci * disclaimer. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 208c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 218c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 228c2ecf20Sopenharmony_ci * provided with the distribution. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 318c2ecf20Sopenharmony_ci * SOFTWARE. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/errno.h> 358c2ecf20Sopenharmony_ci#include <linux/mm.h> 368c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 378c2ecf20Sopenharmony_ci#include <linux/slab.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <linux/mlx4/cmd.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "mlx4.h" 428c2ecf20Sopenharmony_ci#include "icm.h" 438c2ecf20Sopenharmony_ci#include "fw.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * We allocate in as big chunks as we can, up to a maximum of 256 KB 478c2ecf20Sopenharmony_ci * per chunk. Note that the chunks are not necessarily in contiguous 488c2ecf20Sopenharmony_ci * physical memory. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cienum { 518c2ecf20Sopenharmony_ci MLX4_ICM_ALLOC_SIZE = 1 << 18, 528c2ecf20Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE = 1 << 18, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void mlx4_free_icm_pages(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int i; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (chunk->nsg > 0) 608c2ecf20Sopenharmony_ci dma_unmap_sg(&dev->persist->pdev->dev, chunk->sg, chunk->npages, 618c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 648c2ecf20Sopenharmony_ci __free_pages(sg_page(&chunk->sg[i]), 658c2ecf20Sopenharmony_ci get_order(chunk->sg[i].length)); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 738c2ecf20Sopenharmony_ci dma_free_coherent(&dev->persist->pdev->dev, 748c2ecf20Sopenharmony_ci chunk->buf[i].size, 758c2ecf20Sopenharmony_ci chunk->buf[i].addr, 768c2ecf20Sopenharmony_ci chunk->buf[i].dma_addr); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct mlx4_icm_chunk *chunk, *tmp; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!icm) 848c2ecf20Sopenharmony_ci return; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) { 878c2ecf20Sopenharmony_ci if (coherent) 888c2ecf20Sopenharmony_ci mlx4_free_icm_coherent(dev, chunk); 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci mlx4_free_icm_pages(dev, chunk); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci kfree(chunk); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci kfree(icm); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, 998c2ecf20Sopenharmony_ci gfp_t gfp_mask, int node) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct page *page; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci page = alloc_pages_node(node, gfp_mask, order); 1048c2ecf20Sopenharmony_ci if (!page) { 1058c2ecf20Sopenharmony_ci page = alloc_pages(gfp_mask, order); 1068c2ecf20Sopenharmony_ci if (!page) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci sg_set_page(mem, page, PAGE_SIZE << order, 0); 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int mlx4_alloc_icm_coherent(struct device *dev, struct mlx4_icm_buf *buf, 1158c2ecf20Sopenharmony_ci int order, gfp_t gfp_mask) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci buf->addr = dma_alloc_coherent(dev, PAGE_SIZE << order, 1188c2ecf20Sopenharmony_ci &buf->dma_addr, gfp_mask); 1198c2ecf20Sopenharmony_ci if (!buf->addr) 1208c2ecf20Sopenharmony_ci return -ENOMEM; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (offset_in_page(buf->addr)) { 1238c2ecf20Sopenharmony_ci dma_free_coherent(dev, PAGE_SIZE << order, buf->addr, 1248c2ecf20Sopenharmony_ci buf->dma_addr); 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci buf->size = PAGE_SIZE << order; 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, 1338c2ecf20Sopenharmony_ci gfp_t gfp_mask, int coherent) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct mlx4_icm *icm; 1368c2ecf20Sopenharmony_ci struct mlx4_icm_chunk *chunk = NULL; 1378c2ecf20Sopenharmony_ci int cur_order; 1388c2ecf20Sopenharmony_ci gfp_t mask; 1398c2ecf20Sopenharmony_ci int ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* We use sg_set_buf for coherent allocs, which assumes low memory */ 1428c2ecf20Sopenharmony_ci BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM)); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci icm = kmalloc_node(sizeof(*icm), 1458c2ecf20Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN), 1468c2ecf20Sopenharmony_ci dev->numa_node); 1478c2ecf20Sopenharmony_ci if (!icm) { 1488c2ecf20Sopenharmony_ci icm = kmalloc(sizeof(*icm), 1498c2ecf20Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 1508c2ecf20Sopenharmony_ci if (!icm) 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci icm->refcount = 0; 1558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&icm->chunk_list); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci cur_order = get_order(MLX4_ICM_ALLOC_SIZE); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci while (npages > 0) { 1608c2ecf20Sopenharmony_ci if (!chunk) { 1618c2ecf20Sopenharmony_ci chunk = kzalloc_node(sizeof(*chunk), 1628c2ecf20Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | 1638c2ecf20Sopenharmony_ci __GFP_NOWARN), 1648c2ecf20Sopenharmony_ci dev->numa_node); 1658c2ecf20Sopenharmony_ci if (!chunk) { 1668c2ecf20Sopenharmony_ci chunk = kzalloc(sizeof(*chunk), 1678c2ecf20Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | 1688c2ecf20Sopenharmony_ci __GFP_NOWARN)); 1698c2ecf20Sopenharmony_ci if (!chunk) 1708c2ecf20Sopenharmony_ci goto fail; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci chunk->coherent = coherent; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!coherent) 1758c2ecf20Sopenharmony_ci sg_init_table(chunk->sg, MLX4_ICM_CHUNK_LEN); 1768c2ecf20Sopenharmony_ci list_add_tail(&chunk->list, &icm->chunk_list); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci while (1 << cur_order > npages) 1808c2ecf20Sopenharmony_ci --cur_order; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mask = gfp_mask; 1838c2ecf20Sopenharmony_ci if (cur_order) 1848c2ecf20Sopenharmony_ci mask &= ~__GFP_DIRECT_RECLAIM; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (coherent) 1878c2ecf20Sopenharmony_ci ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev, 1888c2ecf20Sopenharmony_ci &chunk->buf[chunk->npages], 1898c2ecf20Sopenharmony_ci cur_order, mask); 1908c2ecf20Sopenharmony_ci else 1918c2ecf20Sopenharmony_ci ret = mlx4_alloc_icm_pages(&chunk->sg[chunk->npages], 1928c2ecf20Sopenharmony_ci cur_order, mask, 1938c2ecf20Sopenharmony_ci dev->numa_node); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (ret) { 1968c2ecf20Sopenharmony_ci if (--cur_order < 0) 1978c2ecf20Sopenharmony_ci goto fail; 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci continue; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ++chunk->npages; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (coherent) 2058c2ecf20Sopenharmony_ci ++chunk->nsg; 2068c2ecf20Sopenharmony_ci else if (chunk->npages == MLX4_ICM_CHUNK_LEN) { 2078c2ecf20Sopenharmony_ci chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, 2088c2ecf20Sopenharmony_ci chunk->sg, chunk->npages, 2098c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (chunk->nsg <= 0) 2128c2ecf20Sopenharmony_ci goto fail; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (chunk->npages == MLX4_ICM_CHUNK_LEN) 2168c2ecf20Sopenharmony_ci chunk = NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci npages -= 1 << cur_order; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!coherent && chunk) { 2228c2ecf20Sopenharmony_ci chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, chunk->sg, 2238c2ecf20Sopenharmony_ci chunk->npages, DMA_BIDIRECTIONAL); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (chunk->nsg <= 0) 2268c2ecf20Sopenharmony_ci goto fail; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return icm; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cifail: 2328c2ecf20Sopenharmony_ci mlx4_free_icm(dev, icm, coherent); 2338c2ecf20Sopenharmony_ci return NULL; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int mlx4_MAP_ICM(struct mlx4_dev *dev, struct mlx4_icm *icm, u64 virt) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM, icm, virt); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int mlx4_UNMAP_ICM(struct mlx4_dev *dev, u64 virt, u32 page_count) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci return mlx4_cmd(dev, virt, page_count, 0, MLX4_CMD_UNMAP_ICM, 2448c2ecf20Sopenharmony_ci MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciint mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM_AUX, icm, -1); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciint mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_UNMAP_ICM_AUX, 2558c2ecf20Sopenharmony_ci MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciint mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u32 i = (obj & (table->num_obj - 1)) / 2618c2ecf20Sopenharmony_ci (MLX4_TABLE_CHUNK_SIZE / table->obj_size); 2628c2ecf20Sopenharmony_ci int ret = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_lock(&table->mutex); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (table->icm[i]) { 2678c2ecf20Sopenharmony_ci ++table->icm[i]->refcount; 2688c2ecf20Sopenharmony_ci goto out; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci table->icm[i] = mlx4_alloc_icm(dev, MLX4_TABLE_CHUNK_SIZE >> PAGE_SHIFT, 2728c2ecf20Sopenharmony_ci (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 2738c2ecf20Sopenharmony_ci __GFP_NOWARN, table->coherent); 2748c2ecf20Sopenharmony_ci if (!table->icm[i]) { 2758c2ecf20Sopenharmony_ci ret = -ENOMEM; 2768c2ecf20Sopenharmony_ci goto out; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (mlx4_MAP_ICM(dev, table->icm[i], table->virt + 2808c2ecf20Sopenharmony_ci (u64) i * MLX4_TABLE_CHUNK_SIZE)) { 2818c2ecf20Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 2828c2ecf20Sopenharmony_ci table->icm[i] = NULL; 2838c2ecf20Sopenharmony_ci ret = -ENOMEM; 2848c2ecf20Sopenharmony_ci goto out; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ++table->icm[i]->refcount; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciout: 2908c2ecf20Sopenharmony_ci mutex_unlock(&table->mutex); 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_civoid mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci u32 i; 2978c2ecf20Sopenharmony_ci u64 offset; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mutex_lock(&table->mutex); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (--table->icm[i]->refcount == 0) { 3048c2ecf20Sopenharmony_ci offset = (u64) i * MLX4_TABLE_CHUNK_SIZE; 3058c2ecf20Sopenharmony_ci mlx4_UNMAP_ICM(dev, table->virt + offset, 3068c2ecf20Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 3078c2ecf20Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 3088c2ecf20Sopenharmony_ci table->icm[i] = NULL; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mutex_unlock(&table->mutex); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, 3158c2ecf20Sopenharmony_ci dma_addr_t *dma_handle) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci int offset, dma_offset, i; 3188c2ecf20Sopenharmony_ci u64 idx; 3198c2ecf20Sopenharmony_ci struct mlx4_icm_chunk *chunk; 3208c2ecf20Sopenharmony_ci struct mlx4_icm *icm; 3218c2ecf20Sopenharmony_ci void *addr = NULL; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!table->lowmem) 3248c2ecf20Sopenharmony_ci return NULL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci mutex_lock(&table->mutex); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size; 3298c2ecf20Sopenharmony_ci icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE]; 3308c2ecf20Sopenharmony_ci dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!icm) 3338c2ecf20Sopenharmony_ci goto out; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci list_for_each_entry(chunk, &icm->chunk_list, list) { 3368c2ecf20Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) { 3378c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3388c2ecf20Sopenharmony_ci size_t len; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (table->coherent) { 3418c2ecf20Sopenharmony_ci len = chunk->buf[i].size; 3428c2ecf20Sopenharmony_ci dma_addr = chunk->buf[i].dma_addr; 3438c2ecf20Sopenharmony_ci addr = chunk->buf[i].addr; 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci struct page *page; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci len = sg_dma_len(&chunk->sg[i]); 3488c2ecf20Sopenharmony_ci dma_addr = sg_dma_address(&chunk->sg[i]); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* XXX: we should never do this for highmem 3518c2ecf20Sopenharmony_ci * allocation. This function either needs 3528c2ecf20Sopenharmony_ci * to be split, or the kernel virtual address 3538c2ecf20Sopenharmony_ci * return needs to be made optional. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci page = sg_page(&chunk->sg[i]); 3568c2ecf20Sopenharmony_ci addr = lowmem_page_address(page); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (dma_handle && dma_offset >= 0) { 3608c2ecf20Sopenharmony_ci if (len > dma_offset) 3618c2ecf20Sopenharmony_ci *dma_handle = dma_addr + dma_offset; 3628c2ecf20Sopenharmony_ci dma_offset -= len; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * DMA mapping can merge pages but not split them, 3678c2ecf20Sopenharmony_ci * so if we found the page, dma_handle has already 3688c2ecf20Sopenharmony_ci * been assigned to. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci if (len > offset) 3718c2ecf20Sopenharmony_ci goto out; 3728c2ecf20Sopenharmony_ci offset -= len; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci addr = NULL; 3778c2ecf20Sopenharmony_ciout: 3788c2ecf20Sopenharmony_ci mutex_unlock(&table->mutex); 3798c2ecf20Sopenharmony_ci return addr ? addr + offset : NULL; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciint mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, 3838c2ecf20Sopenharmony_ci u32 start, u32 end) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size; 3868c2ecf20Sopenharmony_ci int err; 3878c2ecf20Sopenharmony_ci u32 i; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci for (i = start; i <= end; i += inc) { 3908c2ecf20Sopenharmony_ci err = mlx4_table_get(dev, table, i); 3918c2ecf20Sopenharmony_ci if (err) 3928c2ecf20Sopenharmony_ci goto fail; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cifail: 3988c2ecf20Sopenharmony_ci while (i > start) { 3998c2ecf20Sopenharmony_ci i -= inc; 4008c2ecf20Sopenharmony_ci mlx4_table_put(dev, table, i); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return err; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_civoid mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, 4078c2ecf20Sopenharmony_ci u32 start, u32 end) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci u32 i; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size) 4128c2ecf20Sopenharmony_ci mlx4_table_put(dev, table, i); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciint mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, 4168c2ecf20Sopenharmony_ci u64 virt, int obj_size, u32 nobj, int reserved, 4178c2ecf20Sopenharmony_ci int use_lowmem, int use_coherent) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int obj_per_chunk; 4208c2ecf20Sopenharmony_ci int num_icm; 4218c2ecf20Sopenharmony_ci unsigned chunk_size; 4228c2ecf20Sopenharmony_ci int i; 4238c2ecf20Sopenharmony_ci u64 size; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size; 4268c2ecf20Sopenharmony_ci if (WARN_ON(!obj_per_chunk)) 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci num_icm = DIV_ROUND_UP(nobj, obj_per_chunk); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci table->icm = kvcalloc(num_icm, sizeof(*table->icm), GFP_KERNEL); 4318c2ecf20Sopenharmony_ci if (!table->icm) 4328c2ecf20Sopenharmony_ci return -ENOMEM; 4338c2ecf20Sopenharmony_ci table->virt = virt; 4348c2ecf20Sopenharmony_ci table->num_icm = num_icm; 4358c2ecf20Sopenharmony_ci table->num_obj = nobj; 4368c2ecf20Sopenharmony_ci table->obj_size = obj_size; 4378c2ecf20Sopenharmony_ci table->lowmem = use_lowmem; 4388c2ecf20Sopenharmony_ci table->coherent = use_coherent; 4398c2ecf20Sopenharmony_ci mutex_init(&table->mutex); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci size = (u64) nobj * obj_size; 4428c2ecf20Sopenharmony_ci for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) { 4438c2ecf20Sopenharmony_ci chunk_size = MLX4_TABLE_CHUNK_SIZE; 4448c2ecf20Sopenharmony_ci if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > size) 4458c2ecf20Sopenharmony_ci chunk_size = PAGE_ALIGN(size - 4468c2ecf20Sopenharmony_ci i * MLX4_TABLE_CHUNK_SIZE); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT, 4498c2ecf20Sopenharmony_ci (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 4508c2ecf20Sopenharmony_ci __GFP_NOWARN, use_coherent); 4518c2ecf20Sopenharmony_ci if (!table->icm[i]) 4528c2ecf20Sopenharmony_ci goto err; 4538c2ecf20Sopenharmony_ci if (mlx4_MAP_ICM(dev, table->icm[i], virt + i * MLX4_TABLE_CHUNK_SIZE)) { 4548c2ecf20Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], use_coherent); 4558c2ecf20Sopenharmony_ci table->icm[i] = NULL; 4568c2ecf20Sopenharmony_ci goto err; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * Add a reference to this ICM chunk so that it never 4618c2ecf20Sopenharmony_ci * gets freed (since it contains reserved firmware objects). 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci ++table->icm[i]->refcount; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cierr: 4698c2ecf20Sopenharmony_ci for (i = 0; i < num_icm; ++i) 4708c2ecf20Sopenharmony_ci if (table->icm[i]) { 4718c2ecf20Sopenharmony_ci mlx4_UNMAP_ICM(dev, virt + i * MLX4_TABLE_CHUNK_SIZE, 4728c2ecf20Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 4738c2ecf20Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], use_coherent); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci kvfree(table->icm); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return -ENOMEM; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci int i; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci for (i = 0; i < table->num_icm; ++i) 4868c2ecf20Sopenharmony_ci if (table->icm[i]) { 4878c2ecf20Sopenharmony_ci mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE, 4888c2ecf20Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 4898c2ecf20Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci kvfree(table->icm); 4938c2ecf20Sopenharmony_ci} 494