162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2006, 2007 Cisco Systems, Inc. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/errno.h> 3562306a36Sopenharmony_ci#include <linux/mm.h> 3662306a36Sopenharmony_ci#include <linux/scatterlist.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/mlx4/cmd.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "mlx4.h" 4262306a36Sopenharmony_ci#include "icm.h" 4362306a36Sopenharmony_ci#include "fw.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * We allocate in as big chunks as we can, up to a maximum of 256 KB 4762306a36Sopenharmony_ci * per chunk. Note that the chunks are not necessarily in contiguous 4862306a36Sopenharmony_ci * physical memory. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cienum { 5162306a36Sopenharmony_ci MLX4_ICM_ALLOC_SIZE = 1 << 18, 5262306a36Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE = 1 << 18, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void mlx4_free_icm_pages(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (chunk->nsg > 0) 6062306a36Sopenharmony_ci dma_unmap_sg(&dev->persist->pdev->dev, chunk->sg, chunk->npages, 6162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 6462306a36Sopenharmony_ci __free_pages(sg_page(&chunk->sg[i]), 6562306a36Sopenharmony_ci get_order(chunk->sg[i].length)); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int i; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 7362306a36Sopenharmony_ci dma_free_coherent(&dev->persist->pdev->dev, 7462306a36Sopenharmony_ci chunk->buf[i].size, 7562306a36Sopenharmony_ci chunk->buf[i].addr, 7662306a36Sopenharmony_ci chunk->buf[i].dma_addr); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct mlx4_icm_chunk *chunk, *tmp; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!icm) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) { 8762306a36Sopenharmony_ci if (coherent) 8862306a36Sopenharmony_ci mlx4_free_icm_coherent(dev, chunk); 8962306a36Sopenharmony_ci else 9062306a36Sopenharmony_ci mlx4_free_icm_pages(dev, chunk); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci kfree(chunk); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci kfree(icm); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, 9962306a36Sopenharmony_ci gfp_t gfp_mask, int node) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct page *page; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci page = alloc_pages_node(node, gfp_mask, order); 10462306a36Sopenharmony_ci if (!page) { 10562306a36Sopenharmony_ci page = alloc_pages(gfp_mask, order); 10662306a36Sopenharmony_ci if (!page) 10762306a36Sopenharmony_ci return -ENOMEM; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci sg_set_page(mem, page, PAGE_SIZE << order, 0); 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int mlx4_alloc_icm_coherent(struct device *dev, struct mlx4_icm_buf *buf, 11562306a36Sopenharmony_ci int order, gfp_t gfp_mask) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci buf->addr = dma_alloc_coherent(dev, PAGE_SIZE << order, 11862306a36Sopenharmony_ci &buf->dma_addr, gfp_mask); 11962306a36Sopenharmony_ci if (!buf->addr) 12062306a36Sopenharmony_ci return -ENOMEM; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (offset_in_page(buf->addr)) { 12362306a36Sopenharmony_ci dma_free_coherent(dev, PAGE_SIZE << order, buf->addr, 12462306a36Sopenharmony_ci buf->dma_addr); 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci buf->size = PAGE_SIZE << order; 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistruct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, 13362306a36Sopenharmony_ci gfp_t gfp_mask, int coherent) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct mlx4_icm *icm; 13662306a36Sopenharmony_ci struct mlx4_icm_chunk *chunk = NULL; 13762306a36Sopenharmony_ci int cur_order; 13862306a36Sopenharmony_ci gfp_t mask; 13962306a36Sopenharmony_ci int ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* We use sg_set_buf for coherent allocs, which assumes low memory */ 14262306a36Sopenharmony_ci BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci icm = kmalloc_node(sizeof(*icm), 14562306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN), 14662306a36Sopenharmony_ci dev->numa_node); 14762306a36Sopenharmony_ci if (!icm) { 14862306a36Sopenharmony_ci icm = kmalloc(sizeof(*icm), 14962306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 15062306a36Sopenharmony_ci if (!icm) 15162306a36Sopenharmony_ci return NULL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci icm->refcount = 0; 15562306a36Sopenharmony_ci INIT_LIST_HEAD(&icm->chunk_list); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci cur_order = get_order(MLX4_ICM_ALLOC_SIZE); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci while (npages > 0) { 16062306a36Sopenharmony_ci if (!chunk) { 16162306a36Sopenharmony_ci chunk = kzalloc_node(sizeof(*chunk), 16262306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | 16362306a36Sopenharmony_ci __GFP_NOWARN), 16462306a36Sopenharmony_ci dev->numa_node); 16562306a36Sopenharmony_ci if (!chunk) { 16662306a36Sopenharmony_ci chunk = kzalloc(sizeof(*chunk), 16762306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | 16862306a36Sopenharmony_ci __GFP_NOWARN)); 16962306a36Sopenharmony_ci if (!chunk) 17062306a36Sopenharmony_ci goto fail; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci chunk->coherent = coherent; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!coherent) 17562306a36Sopenharmony_ci sg_init_table(chunk->sg, MLX4_ICM_CHUNK_LEN); 17662306a36Sopenharmony_ci list_add_tail(&chunk->list, &icm->chunk_list); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci while (1 << cur_order > npages) 18062306a36Sopenharmony_ci --cur_order; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mask = gfp_mask; 18362306a36Sopenharmony_ci if (cur_order) 18462306a36Sopenharmony_ci mask &= ~__GFP_DIRECT_RECLAIM; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (coherent) 18762306a36Sopenharmony_ci ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev, 18862306a36Sopenharmony_ci &chunk->buf[chunk->npages], 18962306a36Sopenharmony_ci cur_order, mask); 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci ret = mlx4_alloc_icm_pages(&chunk->sg[chunk->npages], 19262306a36Sopenharmony_ci cur_order, mask, 19362306a36Sopenharmony_ci dev->numa_node); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (ret) { 19662306a36Sopenharmony_ci if (--cur_order < 0) 19762306a36Sopenharmony_ci goto fail; 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ++chunk->npages; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (coherent) 20562306a36Sopenharmony_ci ++chunk->nsg; 20662306a36Sopenharmony_ci else if (chunk->npages == MLX4_ICM_CHUNK_LEN) { 20762306a36Sopenharmony_ci chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, 20862306a36Sopenharmony_ci chunk->sg, chunk->npages, 20962306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!chunk->nsg) 21262306a36Sopenharmony_ci goto fail; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (chunk->npages == MLX4_ICM_CHUNK_LEN) 21662306a36Sopenharmony_ci chunk = NULL; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci npages -= 1 << cur_order; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!coherent && chunk) { 22262306a36Sopenharmony_ci chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, chunk->sg, 22362306a36Sopenharmony_ci chunk->npages, DMA_BIDIRECTIONAL); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!chunk->nsg) 22662306a36Sopenharmony_ci goto fail; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return icm; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cifail: 23262306a36Sopenharmony_ci mlx4_free_icm(dev, icm, coherent); 23362306a36Sopenharmony_ci return NULL; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int mlx4_MAP_ICM(struct mlx4_dev *dev, struct mlx4_icm *icm, u64 virt) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM, icm, virt); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int mlx4_UNMAP_ICM(struct mlx4_dev *dev, u64 virt, u32 page_count) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci return mlx4_cmd(dev, virt, page_count, 0, MLX4_CMD_UNMAP_ICM, 24462306a36Sopenharmony_ci MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciint mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM_AUX, icm, -1); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_UNMAP_ICM_AUX, 25562306a36Sopenharmony_ci MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciint mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci u32 i = (obj & (table->num_obj - 1)) / 26162306a36Sopenharmony_ci (MLX4_TABLE_CHUNK_SIZE / table->obj_size); 26262306a36Sopenharmony_ci int ret = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci mutex_lock(&table->mutex); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (table->icm[i]) { 26762306a36Sopenharmony_ci ++table->icm[i]->refcount; 26862306a36Sopenharmony_ci goto out; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci table->icm[i] = mlx4_alloc_icm(dev, MLX4_TABLE_CHUNK_SIZE >> PAGE_SHIFT, 27262306a36Sopenharmony_ci (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 27362306a36Sopenharmony_ci __GFP_NOWARN, table->coherent); 27462306a36Sopenharmony_ci if (!table->icm[i]) { 27562306a36Sopenharmony_ci ret = -ENOMEM; 27662306a36Sopenharmony_ci goto out; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (mlx4_MAP_ICM(dev, table->icm[i], table->virt + 28062306a36Sopenharmony_ci (u64) i * MLX4_TABLE_CHUNK_SIZE)) { 28162306a36Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 28262306a36Sopenharmony_ci table->icm[i] = NULL; 28362306a36Sopenharmony_ci ret = -ENOMEM; 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ++table->icm[i]->refcount; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciout: 29062306a36Sopenharmony_ci mutex_unlock(&table->mutex); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci u32 i; 29762306a36Sopenharmony_ci u64 offset; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci mutex_lock(&table->mutex); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (--table->icm[i]->refcount == 0) { 30462306a36Sopenharmony_ci offset = (u64) i * MLX4_TABLE_CHUNK_SIZE; 30562306a36Sopenharmony_ci mlx4_UNMAP_ICM(dev, table->virt + offset, 30662306a36Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 30762306a36Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 30862306a36Sopenharmony_ci table->icm[i] = NULL; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci mutex_unlock(&table->mutex); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_civoid *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, 31562306a36Sopenharmony_ci dma_addr_t *dma_handle) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int offset, dma_offset, i; 31862306a36Sopenharmony_ci u64 idx; 31962306a36Sopenharmony_ci struct mlx4_icm_chunk *chunk; 32062306a36Sopenharmony_ci struct mlx4_icm *icm; 32162306a36Sopenharmony_ci void *addr = NULL; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!table->lowmem) 32462306a36Sopenharmony_ci return NULL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci mutex_lock(&table->mutex); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size; 32962306a36Sopenharmony_ci icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE]; 33062306a36Sopenharmony_ci dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!icm) 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci list_for_each_entry(chunk, &icm->chunk_list, list) { 33662306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) { 33762306a36Sopenharmony_ci dma_addr_t dma_addr; 33862306a36Sopenharmony_ci size_t len; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (table->coherent) { 34162306a36Sopenharmony_ci len = chunk->buf[i].size; 34262306a36Sopenharmony_ci dma_addr = chunk->buf[i].dma_addr; 34362306a36Sopenharmony_ci addr = chunk->buf[i].addr; 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci struct page *page; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci len = sg_dma_len(&chunk->sg[i]); 34862306a36Sopenharmony_ci dma_addr = sg_dma_address(&chunk->sg[i]); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* XXX: we should never do this for highmem 35162306a36Sopenharmony_ci * allocation. This function either needs 35262306a36Sopenharmony_ci * to be split, or the kernel virtual address 35362306a36Sopenharmony_ci * return needs to be made optional. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci page = sg_page(&chunk->sg[i]); 35662306a36Sopenharmony_ci addr = lowmem_page_address(page); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (dma_handle && dma_offset >= 0) { 36062306a36Sopenharmony_ci if (len > dma_offset) 36162306a36Sopenharmony_ci *dma_handle = dma_addr + dma_offset; 36262306a36Sopenharmony_ci dma_offset -= len; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * DMA mapping can merge pages but not split them, 36762306a36Sopenharmony_ci * so if we found the page, dma_handle has already 36862306a36Sopenharmony_ci * been assigned to. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci if (len > offset) 37162306a36Sopenharmony_ci goto out; 37262306a36Sopenharmony_ci offset -= len; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci addr = NULL; 37762306a36Sopenharmony_ciout: 37862306a36Sopenharmony_ci mutex_unlock(&table->mutex); 37962306a36Sopenharmony_ci return addr ? addr + offset : NULL; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, 38362306a36Sopenharmony_ci u32 start, u32 end) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size; 38662306a36Sopenharmony_ci int err; 38762306a36Sopenharmony_ci u32 i; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (i = start; i <= end; i += inc) { 39062306a36Sopenharmony_ci err = mlx4_table_get(dev, table, i); 39162306a36Sopenharmony_ci if (err) 39262306a36Sopenharmony_ci goto fail; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cifail: 39862306a36Sopenharmony_ci while (i > start) { 39962306a36Sopenharmony_ci i -= inc; 40062306a36Sopenharmony_ci mlx4_table_put(dev, table, i); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return err; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_civoid mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, 40762306a36Sopenharmony_ci u32 start, u32 end) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci u32 i; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size) 41262306a36Sopenharmony_ci mlx4_table_put(dev, table, i); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciint mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, 41662306a36Sopenharmony_ci u64 virt, int obj_size, u32 nobj, int reserved, 41762306a36Sopenharmony_ci int use_lowmem, int use_coherent) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int obj_per_chunk; 42062306a36Sopenharmony_ci int num_icm; 42162306a36Sopenharmony_ci unsigned chunk_size; 42262306a36Sopenharmony_ci int i; 42362306a36Sopenharmony_ci u64 size; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size; 42662306a36Sopenharmony_ci if (WARN_ON(!obj_per_chunk)) 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci num_icm = DIV_ROUND_UP(nobj, obj_per_chunk); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci table->icm = kvcalloc(num_icm, sizeof(*table->icm), GFP_KERNEL); 43162306a36Sopenharmony_ci if (!table->icm) 43262306a36Sopenharmony_ci return -ENOMEM; 43362306a36Sopenharmony_ci table->virt = virt; 43462306a36Sopenharmony_ci table->num_icm = num_icm; 43562306a36Sopenharmony_ci table->num_obj = nobj; 43662306a36Sopenharmony_ci table->obj_size = obj_size; 43762306a36Sopenharmony_ci table->lowmem = use_lowmem; 43862306a36Sopenharmony_ci table->coherent = use_coherent; 43962306a36Sopenharmony_ci mutex_init(&table->mutex); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci size = (u64) nobj * obj_size; 44262306a36Sopenharmony_ci for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) { 44362306a36Sopenharmony_ci chunk_size = MLX4_TABLE_CHUNK_SIZE; 44462306a36Sopenharmony_ci if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > size) 44562306a36Sopenharmony_ci chunk_size = PAGE_ALIGN(size - 44662306a36Sopenharmony_ci i * MLX4_TABLE_CHUNK_SIZE); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT, 44962306a36Sopenharmony_ci (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) | 45062306a36Sopenharmony_ci __GFP_NOWARN, use_coherent); 45162306a36Sopenharmony_ci if (!table->icm[i]) 45262306a36Sopenharmony_ci goto err; 45362306a36Sopenharmony_ci if (mlx4_MAP_ICM(dev, table->icm[i], virt + i * MLX4_TABLE_CHUNK_SIZE)) { 45462306a36Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], use_coherent); 45562306a36Sopenharmony_ci table->icm[i] = NULL; 45662306a36Sopenharmony_ci goto err; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * Add a reference to this ICM chunk so that it never 46162306a36Sopenharmony_ci * gets freed (since it contains reserved firmware objects). 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci ++table->icm[i]->refcount; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cierr: 46962306a36Sopenharmony_ci for (i = 0; i < num_icm; ++i) 47062306a36Sopenharmony_ci if (table->icm[i]) { 47162306a36Sopenharmony_ci mlx4_UNMAP_ICM(dev, virt + i * MLX4_TABLE_CHUNK_SIZE, 47262306a36Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 47362306a36Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], use_coherent); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci kvfree(table->icm); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return -ENOMEM; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_civoid mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci int i; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (i = 0; i < table->num_icm; ++i) 48662306a36Sopenharmony_ci if (table->icm[i]) { 48762306a36Sopenharmony_ci mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE, 48862306a36Sopenharmony_ci MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE); 48962306a36Sopenharmony_ci mlx4_free_icm(dev, table->icm[i], table->coherent); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci kvfree(table->icm); 49362306a36Sopenharmony_ci} 494