linux/drivers/net/ethernet/mellanox/mlx4/icm.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies. All rights reserved.
   3 * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include <linux/errno.h>
  35#include <linux/mm.h>
  36#include <linux/scatterlist.h>
  37#include <linux/slab.h>
  38
  39#include <linux/mlx4/cmd.h>
  40
  41#include "mlx4.h"
  42#include "icm.h"
  43#include "fw.h"
  44
  45/*
  46 * We allocate in as big chunks as we can, up to a maximum of 256 KB
  47 * per chunk. Note that the chunks are not necessarily in contiguous
  48 * physical memory.
  49 */
  50enum {
  51        MLX4_ICM_ALLOC_SIZE     = 1 << 18,
  52        MLX4_TABLE_CHUNK_SIZE   = 1 << 18,
  53};
  54
  55static void mlx4_free_icm_pages(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk)
  56{
  57        int i;
  58
  59        if (chunk->nsg > 0)
  60                dma_unmap_sg(&dev->persist->pdev->dev, chunk->sg, chunk->npages,
  61                             DMA_BIDIRECTIONAL);
  62
  63        for (i = 0; i < chunk->npages; ++i)
  64                __free_pages(sg_page(&chunk->sg[i]),
  65                             get_order(chunk->sg[i].length));
  66}
  67
  68static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk)
  69{
  70        int i;
  71
  72        for (i = 0; i < chunk->npages; ++i)
  73                dma_free_coherent(&dev->persist->pdev->dev,
  74                                  chunk->buf[i].size,
  75                                  chunk->buf[i].addr,
  76                                  chunk->buf[i].dma_addr);
  77}
  78
  79void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent)
  80{
  81        struct mlx4_icm_chunk *chunk, *tmp;
  82
  83        if (!icm)
  84                return;
  85
  86        list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) {
  87                if (coherent)
  88                        mlx4_free_icm_coherent(dev, chunk);
  89                else
  90                        mlx4_free_icm_pages(dev, chunk);
  91
  92                kfree(chunk);
  93        }
  94
  95        kfree(icm);
  96}
  97
  98static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order,
  99                                gfp_t gfp_mask, int node)
 100{
 101        struct page *page;
 102
 103        page = alloc_pages_node(node, gfp_mask, order);
 104        if (!page) {
 105                page = alloc_pages(gfp_mask, order);
 106                if (!page)
 107                        return -ENOMEM;
 108        }
 109
 110        sg_set_page(mem, page, PAGE_SIZE << order, 0);
 111        return 0;
 112}
 113
 114static int mlx4_alloc_icm_coherent(struct device *dev, struct mlx4_icm_buf *buf,
 115                                   int order, gfp_t gfp_mask)
 116{
 117        buf->addr = dma_alloc_coherent(dev, PAGE_SIZE << order,
 118                                       &buf->dma_addr, gfp_mask);
 119        if (!buf->addr)
 120                return -ENOMEM;
 121
 122        if (offset_in_page(buf->addr)) {
 123                dma_free_coherent(dev, PAGE_SIZE << order, buf->addr,
 124                                  buf->dma_addr);
 125                return -ENOMEM;
 126        }
 127
 128        buf->size = PAGE_SIZE << order;
 129        return 0;
 130}
 131
 132struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
 133                                gfp_t gfp_mask, int coherent)
 134{
 135        struct mlx4_icm *icm;
 136        struct mlx4_icm_chunk *chunk = NULL;
 137        int cur_order;
 138        gfp_t mask;
 139        int ret;
 140
 141        /* We use sg_set_buf for coherent allocs, which assumes low memory */
 142        BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM));
 143
 144        icm = kmalloc_node(sizeof(*icm),
 145                           gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN),
 146                           dev->numa_node);
 147        if (!icm) {
 148                icm = kmalloc(sizeof(*icm),
 149                              gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
 150                if (!icm)
 151                        return NULL;
 152        }
 153
 154        icm->refcount = 0;
 155        INIT_LIST_HEAD(&icm->chunk_list);
 156
 157        cur_order = get_order(MLX4_ICM_ALLOC_SIZE);
 158
 159        while (npages > 0) {
 160                if (!chunk) {
 161                        chunk = kzalloc_node(sizeof(*chunk),
 162                                             gfp_mask & ~(__GFP_HIGHMEM |
 163                                                          __GFP_NOWARN),
 164                                             dev->numa_node);
 165                        if (!chunk) {
 166                                chunk = kzalloc(sizeof(*chunk),
 167                                                gfp_mask & ~(__GFP_HIGHMEM |
 168                                                             __GFP_NOWARN));
 169                                if (!chunk)
 170                                        goto fail;
 171                        }
 172                        chunk->coherent = coherent;
 173
 174                        if (!coherent)
 175                                sg_init_table(chunk->sg, MLX4_ICM_CHUNK_LEN);
 176                        list_add_tail(&chunk->list, &icm->chunk_list);
 177                }
 178
 179                while (1 << cur_order > npages)
 180                        --cur_order;
 181
 182                mask = gfp_mask;
 183                if (cur_order)
 184                        mask &= ~__GFP_DIRECT_RECLAIM;
 185
 186                if (coherent)
 187                        ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev,
 188                                                &chunk->buf[chunk->npages],
 189                                                cur_order, mask);
 190                else
 191                        ret = mlx4_alloc_icm_pages(&chunk->sg[chunk->npages],
 192                                                   cur_order, mask,
 193                                                   dev->numa_node);
 194
 195                if (ret) {
 196                        if (--cur_order < 0)
 197                                goto fail;
 198                        else
 199                                continue;
 200                }
 201
 202                ++chunk->npages;
 203
 204                if (coherent)
 205                        ++chunk->nsg;
 206                else if (chunk->npages == MLX4_ICM_CHUNK_LEN) {
 207                        chunk->nsg = dma_map_sg(&dev->persist->pdev->dev,
 208                                                chunk->sg, chunk->npages,
 209                                                DMA_BIDIRECTIONAL);
 210
 211                        if (chunk->nsg <= 0)
 212                                goto fail;
 213                }
 214
 215                if (chunk->npages == MLX4_ICM_CHUNK_LEN)
 216                        chunk = NULL;
 217
 218                npages -= 1 << cur_order;
 219        }
 220
 221        if (!coherent && chunk) {
 222                chunk->nsg = dma_map_sg(&dev->persist->pdev->dev, chunk->sg,
 223                                        chunk->npages, DMA_BIDIRECTIONAL);
 224
 225                if (chunk->nsg <= 0)
 226                        goto fail;
 227        }
 228
 229        return icm;
 230
 231fail:
 232        mlx4_free_icm(dev, icm, coherent);
 233        return NULL;
 234}
 235
 236static int mlx4_MAP_ICM(struct mlx4_dev *dev, struct mlx4_icm *icm, u64 virt)
 237{
 238        return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM, icm, virt);
 239}
 240
 241static int mlx4_UNMAP_ICM(struct mlx4_dev *dev, u64 virt, u32 page_count)
 242{
 243        return mlx4_cmd(dev, virt, page_count, 0, MLX4_CMD_UNMAP_ICM,
 244                        MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
 245}
 246
 247int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm)
 248{
 249        return mlx4_map_cmd(dev, MLX4_CMD_MAP_ICM_AUX, icm, -1);
 250}
 251
 252int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev)
 253{
 254        return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_UNMAP_ICM_AUX,
 255                        MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
 256}
 257
 258int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
 259{
 260        u32 i = (obj & (table->num_obj - 1)) /
 261                        (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
 262        int ret = 0;
 263
 264        mutex_lock(&table->mutex);
 265
 266        if (table->icm[i]) {
 267                ++table->icm[i]->refcount;
 268                goto out;
 269        }
 270
 271        table->icm[i] = mlx4_alloc_icm(dev, MLX4_TABLE_CHUNK_SIZE >> PAGE_SHIFT,
 272                                       (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
 273                                       __GFP_NOWARN, table->coherent);
 274        if (!table->icm[i]) {
 275                ret = -ENOMEM;
 276                goto out;
 277        }
 278
 279        if (mlx4_MAP_ICM(dev, table->icm[i], table->virt +
 280                         (u64) i * MLX4_TABLE_CHUNK_SIZE)) {
 281                mlx4_free_icm(dev, table->icm[i], table->coherent);
 282                table->icm[i] = NULL;
 283                ret = -ENOMEM;
 284                goto out;
 285        }
 286
 287        ++table->icm[i]->refcount;
 288
 289out:
 290        mutex_unlock(&table->mutex);
 291        return ret;
 292}
 293
 294void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
 295{
 296        u32 i;
 297        u64 offset;
 298
 299        i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
 300
 301        mutex_lock(&table->mutex);
 302
 303        if (--table->icm[i]->refcount == 0) {
 304                offset = (u64) i * MLX4_TABLE_CHUNK_SIZE;
 305                mlx4_UNMAP_ICM(dev, table->virt + offset,
 306                               MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
 307                mlx4_free_icm(dev, table->icm[i], table->coherent);
 308                table->icm[i] = NULL;
 309        }
 310
 311        mutex_unlock(&table->mutex);
 312}
 313
 314void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj,
 315                        dma_addr_t *dma_handle)
 316{
 317        int offset, dma_offset, i;
 318        u64 idx;
 319        struct mlx4_icm_chunk *chunk;
 320        struct mlx4_icm *icm;
 321        void *addr = NULL;
 322
 323        if (!table->lowmem)
 324                return NULL;
 325
 326        mutex_lock(&table->mutex);
 327
 328        idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size;
 329        icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE];
 330        dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE;
 331
 332        if (!icm)
 333                goto out;
 334
 335        list_for_each_entry(chunk, &icm->chunk_list, list) {
 336                for (i = 0; i < chunk->npages; ++i) {
 337                        dma_addr_t dma_addr;
 338                        size_t len;
 339
 340                        if (table->coherent) {
 341                                len = chunk->buf[i].size;
 342                                dma_addr = chunk->buf[i].dma_addr;
 343                                addr = chunk->buf[i].addr;
 344                        } else {
 345                                struct page *page;
 346
 347                                len = sg_dma_len(&chunk->sg[i]);
 348                                dma_addr = sg_dma_address(&chunk->sg[i]);
 349
 350                                /* XXX: we should never do this for highmem
 351                                 * allocation.  This function either needs
 352                                 * to be split, or the kernel virtual address
 353                                 * return needs to be made optional.
 354                                 */
 355                                page = sg_page(&chunk->sg[i]);
 356                                addr = lowmem_page_address(page);
 357                        }
 358
 359                        if (dma_handle && dma_offset >= 0) {
 360                                if (len > dma_offset)
 361                                        *dma_handle = dma_addr + dma_offset;
 362                                dma_offset -= len;
 363                        }
 364
 365                        /*
 366                         * DMA mapping can merge pages but not split them,
 367                         * so if we found the page, dma_handle has already
 368                         * been assigned to.
 369                         */
 370                        if (len > offset)
 371                                goto out;
 372                        offset -= len;
 373                }
 374        }
 375
 376        addr = NULL;
 377out:
 378        mutex_unlock(&table->mutex);
 379        return addr ? addr + offset : NULL;
 380}
 381
 382int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
 383                         u32 start, u32 end)
 384{
 385        int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size;
 386        int err;
 387        u32 i;
 388
 389        for (i = start; i <= end; i += inc) {
 390                err = mlx4_table_get(dev, table, i);
 391                if (err)
 392                        goto fail;
 393        }
 394
 395        return 0;
 396
 397fail:
 398        while (i > start) {
 399                i -= inc;
 400                mlx4_table_put(dev, table, i);
 401        }
 402
 403        return err;
 404}
 405
 406void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
 407                          u32 start, u32 end)
 408{
 409        u32 i;
 410
 411        for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size)
 412                mlx4_table_put(dev, table, i);
 413}
 414
 415int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
 416                        u64 virt, int obj_size, u32 nobj, int reserved,
 417                        int use_lowmem, int use_coherent)
 418{
 419        int obj_per_chunk;
 420        int num_icm;
 421        unsigned chunk_size;
 422        int i;
 423        u64 size;
 424
 425        obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size;
 426        if (WARN_ON(!obj_per_chunk))
 427                return -EINVAL;
 428        num_icm = DIV_ROUND_UP(nobj, obj_per_chunk);
 429
 430        table->icm      = kvcalloc(num_icm, sizeof(*table->icm), GFP_KERNEL);
 431        if (!table->icm)
 432                return -ENOMEM;
 433        table->virt     = virt;
 434        table->num_icm  = num_icm;
 435        table->num_obj  = nobj;
 436        table->obj_size = obj_size;
 437        table->lowmem   = use_lowmem;
 438        table->coherent = use_coherent;
 439        mutex_init(&table->mutex);
 440
 441        size = (u64) nobj * obj_size;
 442        for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) {
 443                chunk_size = MLX4_TABLE_CHUNK_SIZE;
 444                if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > size)
 445                        chunk_size = PAGE_ALIGN(size -
 446                                        i * MLX4_TABLE_CHUNK_SIZE);
 447
 448                table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT,
 449                                               (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
 450                                               __GFP_NOWARN, use_coherent);
 451                if (!table->icm[i])
 452                        goto err;
 453                if (mlx4_MAP_ICM(dev, table->icm[i], virt + i * MLX4_TABLE_CHUNK_SIZE)) {
 454                        mlx4_free_icm(dev, table->icm[i], use_coherent);
 455                        table->icm[i] = NULL;
 456                        goto err;
 457                }
 458
 459                /*
 460                 * Add a reference to this ICM chunk so that it never
 461                 * gets freed (since it contains reserved firmware objects).
 462                 */
 463                ++table->icm[i]->refcount;
 464        }
 465
 466        return 0;
 467
 468err:
 469        for (i = 0; i < num_icm; ++i)
 470                if (table->icm[i]) {
 471                        mlx4_UNMAP_ICM(dev, virt + i * MLX4_TABLE_CHUNK_SIZE,
 472                                       MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
 473                        mlx4_free_icm(dev, table->icm[i], use_coherent);
 474                }
 475
 476        kvfree(table->icm);
 477
 478        return -ENOMEM;
 479}
 480
 481void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table)
 482{
 483        int i;
 484
 485        for (i = 0; i < table->num_icm; ++i)
 486                if (table->icm[i]) {
 487                        mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE,
 488                                       MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
 489                        mlx4_free_icm(dev, table->icm[i], table->coherent);
 490                }
 491
 492        kvfree(table->icm);
 493}
 494