1 /* 2 * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32 33 #include <linux/module.h> 34 #include <rdma/ib_umem.h> 35 #include "mlx5_ib.h" 36 37 /* @umem: umem object to scan 38 * @addr: ib virtual address requested by the user 39 * @count: number of PAGE_SIZE pages covered by umem 40 * @shift: page shift for the compound pages found in the region 41 * @ncont: number of compund pages 42 * @order: log2 of the number of compound pages 43 */ 44 void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift, 45 int *ncont, int *order) 46 { 47 unsigned long tmp; 48 unsigned long m; 49 int i, k; 50 u64 base = 0; 51 int p = 0; 52 int skip; 53 int mask; 54 u64 len; 55 u64 pfn; 56 struct scatterlist *sg; 57 int entry; 58 unsigned long page_shift = ilog2(umem->page_size); 59 60 addr = addr >> page_shift; 61 tmp = (unsigned long)addr; 62 m = find_first_bit(&tmp, sizeof(tmp)); 63 skip = 1 << m; 64 mask = skip - 1; 65 i = 0; 66 for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { 67 len = sg_dma_len(sg) >> page_shift; 68 pfn = sg_dma_address(sg) >> page_shift; 69 for (k = 0; k < len; k++) { 70 if (!(i & mask)) { 71 tmp = (unsigned long)pfn; 72 m = min(m, find_first_bit(&tmp, sizeof(tmp))); 73 skip = 1 << m; 74 mask = skip - 1; 75 base = pfn; 76 p = 0; 77 } else { 78 if (base + p != pfn) { 79 tmp = (unsigned long)p; 80 m = find_first_bit(&tmp, sizeof(tmp)); 81 skip = 1 << m; 82 mask = skip - 1; 83 base = pfn; 84 p = 0; 85 } 86 } 87 p++; 88 i++; 89 } 90 } 91 92 if (i) { 93 m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m); 94 95 if (order) 96 *order = ilog2(roundup_pow_of_two(i) >> m); 97 98 *ncont = DIV_ROUND_UP(i, (1 << m)); 99 } else { 100 m = 0; 101 102 if (order) 103 *order = 0; 104 105 *ncont = 0; 106 } 107 *shift = page_shift + m; 108 *count = i; 109 } 110 111 void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem, 112 int page_shift, __be64 *pas, int umr) 113 { 114 unsigned long umem_page_shift = ilog2(umem->page_size); 115 int shift = page_shift - umem_page_shift; 116 int mask = (1 << shift) - 1; 117 int i, k; 118 u64 cur = 0; 119 u64 base; 120 int len; 121 struct scatterlist *sg; 122 int entry; 123 124 i = 0; 125 for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { 126 len = sg_dma_len(sg) >> umem_page_shift; 127 base = sg_dma_address(sg); 128 for (k = 0; k < len; k++) { 129 if (!(i & mask)) { 130 cur = base + (k << umem_page_shift); 131 if (umr) 132 cur |= 3; 133 134 pas[i >> shift] = cpu_to_be64(cur); 135 mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n", 136 i >> shift, be64_to_cpu(pas[i >> shift])); 137 } else 138 mlx5_ib_dbg(dev, "=====> 0x%llx\n", 139 base + (k << umem_page_shift)); 140 i++; 141 } 142 } 143 } 144 145 int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset) 146 { 147 u64 page_size; 148 u64 page_mask; 149 u64 off_size; 150 u64 off_mask; 151 u64 buf_off; 152 153 page_size = (u64)1 << page_shift; 154 page_mask = page_size - 1; 155 buf_off = addr & page_mask; 156 off_size = page_size >> 6; 157 off_mask = off_size - 1; 158 159 if (buf_off & off_mask) 160 return -EINVAL; 161 162 *offset = buf_off >> ilog2(off_size); 163 return 0; 164 } 165