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 59 addr = addr >> PAGE_SHIFT; 60 tmp = (unsigned long)addr; 61 m = find_first_bit(&tmp, sizeof(tmp)); 62 skip = 1 << m; 63 mask = skip - 1; 64 i = 0; 65 for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { 66 len = sg_dma_len(sg) >> PAGE_SHIFT; 67 pfn = sg_dma_address(sg) >> PAGE_SHIFT; 68 for (k = 0; k < len; k++) { 69 if (!(i & mask)) { 70 tmp = (unsigned long)pfn; 71 m = min(m, find_first_bit(&tmp, sizeof(tmp))); 72 skip = 1 << m; 73 mask = skip - 1; 74 base = pfn; 75 p = 0; 76 } else { 77 if (base + p != pfn) { 78 tmp = (unsigned long)p; 79 m = find_first_bit(&tmp, sizeof(tmp)); 80 skip = 1 << m; 81 mask = skip - 1; 82 base = pfn; 83 p = 0; 84 } 85 } 86 p++; 87 i++; 88 } 89 } 90 91 if (i) { 92 m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m); 93 94 if (order) 95 *order = ilog2(roundup_pow_of_two(i) >> m); 96 97 *ncont = DIV_ROUND_UP(i, (1 << m)); 98 } else { 99 m = 0; 100 101 if (order) 102 *order = 0; 103 104 *ncont = 0; 105 } 106 *shift = PAGE_SHIFT + m; 107 *count = i; 108 } 109 110 void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem, 111 int page_shift, __be64 *pas, int umr) 112 { 113 int shift = page_shift - PAGE_SHIFT; 114 int mask = (1 << shift) - 1; 115 int i, k; 116 u64 cur = 0; 117 u64 base; 118 int len; 119 struct scatterlist *sg; 120 int entry; 121 122 i = 0; 123 for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) { 124 len = sg_dma_len(sg) >> PAGE_SHIFT; 125 base = sg_dma_address(sg); 126 for (k = 0; k < len; k++) { 127 if (!(i & mask)) { 128 cur = base + (k << PAGE_SHIFT); 129 if (umr) 130 cur |= 3; 131 132 pas[i >> shift] = cpu_to_be64(cur); 133 mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n", 134 i >> shift, be64_to_cpu(pas[i >> shift])); 135 } else 136 mlx5_ib_dbg(dev, "=====> 0x%llx\n", 137 base + (k << PAGE_SHIFT)); 138 i++; 139 } 140 } 141 } 142 143 int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset) 144 { 145 u64 page_size; 146 u64 page_mask; 147 u64 off_size; 148 u64 off_mask; 149 u64 buf_off; 150 151 page_size = 1 << page_shift; 152 page_mask = page_size - 1; 153 buf_off = addr & page_mask; 154 off_size = page_size >> 6; 155 off_mask = off_size - 1; 156 157 if (buf_off & off_mask) 158 return -EINVAL; 159 160 *offset = buf_off >> ilog2(off_size); 161 return 0; 162 } 163