linux/drivers/infiniband/hw/mlx5/mem.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013-2015, Mellanox Technologies. 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 <rdma/ib_umem_odp.h>
  36#include "mlx5_ib.h"
  37
  38/* @umem: umem object to scan
  39 * @addr: ib virtual address requested by the user
  40 * @count: number of PAGE_SIZE pages covered by umem
  41 * @shift: page shift for the compound pages found in the region
  42 * @ncont: number of compund pages
  43 * @order: log2 of the number of compound pages
  44 */
  45void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift,
  46                        int *ncont, int *order)
  47{
  48        unsigned long tmp;
  49        unsigned long m;
  50        int i, k;
  51        u64 base = 0;
  52        int p = 0;
  53        int skip;
  54        int mask;
  55        u64 len;
  56        u64 pfn;
  57        struct scatterlist *sg;
  58        int entry;
  59        unsigned long page_shift = ilog2(umem->page_size);
  60
  61        /* With ODP we must always match OS page size. */
  62        if (umem->odp_data) {
  63                *count = ib_umem_page_count(umem);
  64                *shift = PAGE_SHIFT;
  65                *ncont = *count;
  66                if (order)
  67                        *order = ilog2(roundup_pow_of_two(*count));
  68
  69                return;
  70        }
  71
  72        addr = addr >> page_shift;
  73        tmp = (unsigned long)addr;
  74        m = find_first_bit(&tmp, sizeof(tmp));
  75        skip = 1 << m;
  76        mask = skip - 1;
  77        i = 0;
  78        for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
  79                len = sg_dma_len(sg) >> page_shift;
  80                pfn = sg_dma_address(sg) >> page_shift;
  81                for (k = 0; k < len; k++) {
  82                        if (!(i & mask)) {
  83                                tmp = (unsigned long)pfn;
  84                                m = min_t(unsigned long, m, find_first_bit(&tmp, sizeof(tmp)));
  85                                skip = 1 << m;
  86                                mask = skip - 1;
  87                                base = pfn;
  88                                p = 0;
  89                        } else {
  90                                if (base + p != pfn) {
  91                                        tmp = (unsigned long)p;
  92                                        m = find_first_bit(&tmp, sizeof(tmp));
  93                                        skip = 1 << m;
  94                                        mask = skip - 1;
  95                                        base = pfn;
  96                                        p = 0;
  97                                }
  98                        }
  99                        p++;
 100                        i++;
 101                }
 102        }
 103
 104        if (i) {
 105                m = min_t(unsigned long, ilog2(roundup_pow_of_two(i)), m);
 106
 107                if (order)
 108                        *order = ilog2(roundup_pow_of_two(i) >> m);
 109
 110                *ncont = DIV_ROUND_UP(i, (1 << m));
 111        } else {
 112                m  = 0;
 113
 114                if (order)
 115                        *order = 0;
 116
 117                *ncont = 0;
 118        }
 119        *shift = page_shift + m;
 120        *count = i;
 121}
 122
 123#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
 124static u64 umem_dma_to_mtt(dma_addr_t umem_dma)
 125{
 126        u64 mtt_entry = umem_dma & ODP_DMA_ADDR_MASK;
 127
 128        if (umem_dma & ODP_READ_ALLOWED_BIT)
 129                mtt_entry |= MLX5_IB_MTT_READ;
 130        if (umem_dma & ODP_WRITE_ALLOWED_BIT)
 131                mtt_entry |= MLX5_IB_MTT_WRITE;
 132
 133        return mtt_entry;
 134}
 135#endif
 136
 137/*
 138 * Populate the given array with bus addresses from the umem.
 139 *
 140 * dev - mlx5_ib device
 141 * umem - umem to use to fill the pages
 142 * page_shift - determines the page size used in the resulting array
 143 * offset - offset into the umem to start from,
 144 *          only implemented for ODP umems
 145 * num_pages - total number of pages to fill
 146 * pas - bus addresses array to fill
 147 * access_flags - access flags to set on all present pages.
 148                  use enum mlx5_ib_mtt_access_flags for this.
 149 */
 150void __mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
 151                            int page_shift, size_t offset, size_t num_pages,
 152                            __be64 *pas, int access_flags)
 153{
 154        unsigned long umem_page_shift = ilog2(umem->page_size);
 155        int shift = page_shift - umem_page_shift;
 156        int mask = (1 << shift) - 1;
 157        int i, k;
 158        u64 cur = 0;
 159        u64 base;
 160        int len;
 161        struct scatterlist *sg;
 162        int entry;
 163#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING
 164        const bool odp = umem->odp_data != NULL;
 165
 166        if (odp) {
 167                WARN_ON(shift != 0);
 168                WARN_ON(access_flags != (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE));
 169
 170                for (i = 0; i < num_pages; ++i) {
 171                        dma_addr_t pa = umem->odp_data->dma_list[offset + i];
 172
 173                        pas[i] = cpu_to_be64(umem_dma_to_mtt(pa));
 174                }
 175                return;
 176        }
 177#endif
 178
 179        i = 0;
 180        for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
 181                len = sg_dma_len(sg) >> umem_page_shift;
 182                base = sg_dma_address(sg);
 183                for (k = 0; k < len; k++) {
 184                        if (!(i & mask)) {
 185                                cur = base + (k << umem_page_shift);
 186                                cur |= access_flags;
 187
 188                                pas[i >> shift] = cpu_to_be64(cur);
 189                                mlx5_ib_dbg(dev, "pas[%d] 0x%llx\n",
 190                                            i >> shift, be64_to_cpu(pas[i >> shift]));
 191                        }  else
 192                                mlx5_ib_dbg(dev, "=====> 0x%llx\n",
 193                                            base + (k << umem_page_shift));
 194                        i++;
 195                }
 196        }
 197}
 198
 199void mlx5_ib_populate_pas(struct mlx5_ib_dev *dev, struct ib_umem *umem,
 200                          int page_shift, __be64 *pas, int access_flags)
 201{
 202        return __mlx5_ib_populate_pas(dev, umem, page_shift, 0,
 203                                      ib_umem_num_pages(umem), pas,
 204                                      access_flags);
 205}
 206int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset)
 207{
 208        u64 page_size;
 209        u64 page_mask;
 210        u64 off_size;
 211        u64 off_mask;
 212        u64 buf_off;
 213
 214        page_size = (u64)1 << page_shift;
 215        page_mask = page_size - 1;
 216        buf_off = addr & page_mask;
 217        off_size = page_size >> 6;
 218        off_mask = off_size - 1;
 219
 220        if (buf_off & off_mask)
 221                return -EINVAL;
 222
 223        *offset = buf_off >> ilog2(off_size);
 224        return 0;
 225}
 226