1dc7e38acSHans Petter Selasky /*-
21c807f67SHans Petter Selasky * Copyright (c) 2013-2017, Mellanox Technologies, Ltd. All rights reserved.
3dc7e38acSHans Petter Selasky *
4dc7e38acSHans Petter Selasky * Redistribution and use in source and binary forms, with or without
5dc7e38acSHans Petter Selasky * modification, are permitted provided that the following conditions
6dc7e38acSHans Petter Selasky * are met:
7dc7e38acSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright
8dc7e38acSHans Petter Selasky * notice, this list of conditions and the following disclaimer.
9dc7e38acSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright
10dc7e38acSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the
11dc7e38acSHans Petter Selasky * documentation and/or other materials provided with the distribution.
12dc7e38acSHans Petter Selasky *
13dc7e38acSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14dc7e38acSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15dc7e38acSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16dc7e38acSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17dc7e38acSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18dc7e38acSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19dc7e38acSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20dc7e38acSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21dc7e38acSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22dc7e38acSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23dc7e38acSHans Petter Selasky * SUCH DAMAGE.
24dc7e38acSHans Petter Selasky */
25dc7e38acSHans Petter Selasky
26ee9d634bSKonstantin Belousov #include "opt_rss.h"
27ee9d634bSKonstantin Belousov #include "opt_ratelimit.h"
28ee9d634bSKonstantin Belousov
29dc7e38acSHans Petter Selasky #include <linux/errno.h>
30dc7e38acSHans Petter Selasky #include <linux/slab.h>
31dc7e38acSHans Petter Selasky #include <linux/mm.h>
32dc7e38acSHans Petter Selasky #include <linux/dma-mapping.h>
33dc7e38acSHans Petter Selasky #include <linux/vmalloc.h>
34dc7e38acSHans Petter Selasky #include <dev/mlx5/driver.h>
35*12c56d7dSHans Petter Selasky #include <dev/mlx5/mlx5_core/mlx5_core.h>
36dc7e38acSHans Petter Selasky
37dc7e38acSHans Petter Selasky /* Handling for queue buffers -- we allocate a bunch of memory and
38dc7e38acSHans Petter Selasky * register it in a memory region at HCA virtual address 0. If the
39dc7e38acSHans Petter Selasky * requested size is > max_direct, we split the allocation into
40dc7e38acSHans Petter Selasky * multiple pages, so we don't require too much contiguous memory.
41dc7e38acSHans Petter Selasky */
42dc7e38acSHans Petter Selasky
431c807f67SHans Petter Selasky static void
mlx5_buf_load_mem_cb(void * arg,bus_dma_segment_t * segs,int nseg,int error)441c807f67SHans Petter Selasky mlx5_buf_load_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
45dc7e38acSHans Petter Selasky {
461c807f67SHans Petter Selasky struct mlx5_buf *buf;
471c807f67SHans Petter Selasky uint8_t owned;
481c807f67SHans Petter Selasky int x;
49dc7e38acSHans Petter Selasky
501c807f67SHans Petter Selasky buf = (struct mlx5_buf *)arg;
511c807f67SHans Petter Selasky owned = MLX5_DMA_OWNED(buf->dev);
521c807f67SHans Petter Selasky
531c807f67SHans Petter Selasky if (!owned)
541c807f67SHans Petter Selasky MLX5_DMA_LOCK(buf->dev);
551c807f67SHans Petter Selasky
561c807f67SHans Petter Selasky if (error == 0) {
571c807f67SHans Petter Selasky for (x = 0; x != nseg; x++) {
581c807f67SHans Petter Selasky buf->page_list[x] = segs[x].ds_addr;
591c807f67SHans Petter Selasky KASSERT(segs[x].ds_len == PAGE_SIZE, ("Invalid segment size"));
60dc7e38acSHans Petter Selasky }
611c807f67SHans Petter Selasky buf->load_done = MLX5_LOAD_ST_SUCCESS;
62dc7e38acSHans Petter Selasky } else {
631c807f67SHans Petter Selasky buf->load_done = MLX5_LOAD_ST_FAILURE;
641c807f67SHans Petter Selasky }
651c807f67SHans Petter Selasky MLX5_DMA_DONE(buf->dev);
66dc7e38acSHans Petter Selasky
671c807f67SHans Petter Selasky if (!owned)
681c807f67SHans Petter Selasky MLX5_DMA_UNLOCK(buf->dev);
69dc7e38acSHans Petter Selasky }
70dc7e38acSHans Petter Selasky
711c807f67SHans Petter Selasky int
mlx5_buf_alloc(struct mlx5_core_dev * dev,int size,int max_direct,struct mlx5_buf * buf)721c807f67SHans Petter Selasky mlx5_buf_alloc(struct mlx5_core_dev *dev, int size,
731c807f67SHans Petter Selasky int max_direct, struct mlx5_buf *buf)
74dc7e38acSHans Petter Selasky {
751c807f67SHans Petter Selasky int err;
76dc7e38acSHans Petter Selasky
771c807f67SHans Petter Selasky buf->npages = howmany(size, PAGE_SIZE);
781c807f67SHans Petter Selasky buf->page_shift = PAGE_SHIFT;
791c807f67SHans Petter Selasky buf->load_done = MLX5_LOAD_ST_NONE;
801c807f67SHans Petter Selasky buf->dev = dev;
811c807f67SHans Petter Selasky buf->page_list = kcalloc(buf->npages, sizeof(*buf->page_list),
821c807f67SHans Petter Selasky GFP_KERNEL);
831c807f67SHans Petter Selasky
841c807f67SHans Petter Selasky err = -bus_dma_tag_create(
851c807f67SHans Petter Selasky bus_get_dma_tag(dev->pdev->dev.bsddev),
861c807f67SHans Petter Selasky PAGE_SIZE, /* alignment */
871c807f67SHans Petter Selasky 0, /* no boundary */
881c807f67SHans Petter Selasky BUS_SPACE_MAXADDR, /* lowaddr */
891c807f67SHans Petter Selasky BUS_SPACE_MAXADDR, /* highaddr */
901c807f67SHans Petter Selasky NULL, NULL, /* filter, filterarg */
911c807f67SHans Petter Selasky PAGE_SIZE * buf->npages, /* maxsize */
921c807f67SHans Petter Selasky buf->npages, /* nsegments */
931c807f67SHans Petter Selasky PAGE_SIZE, /* maxsegsize */
941c807f67SHans Petter Selasky 0, /* flags */
951c807f67SHans Petter Selasky NULL, NULL, /* lockfunc, lockfuncarg */
961c807f67SHans Petter Selasky &buf->dma_tag);
971c807f67SHans Petter Selasky
981c807f67SHans Petter Selasky if (err != 0)
991c807f67SHans Petter Selasky goto err_dma_tag;
1001c807f67SHans Petter Selasky
1011c807f67SHans Petter Selasky /* allocate memory */
1021c807f67SHans Petter Selasky err = -bus_dmamem_alloc(buf->dma_tag, &buf->direct.buf,
1031c807f67SHans Petter Selasky BUS_DMA_WAITOK | BUS_DMA_COHERENT, &buf->dma_map);
1041c807f67SHans Petter Selasky if (err != 0)
1051c807f67SHans Petter Selasky goto err_dma_alloc;
1061c807f67SHans Petter Selasky
1071c807f67SHans Petter Selasky /* load memory into DMA */
1081c807f67SHans Petter Selasky MLX5_DMA_LOCK(dev);
1091c807f67SHans Petter Selasky err = bus_dmamap_load(
1101c807f67SHans Petter Selasky buf->dma_tag, buf->dma_map, buf->direct.buf,
1111c807f67SHans Petter Selasky PAGE_SIZE * buf->npages, &mlx5_buf_load_mem_cb,
1121c807f67SHans Petter Selasky buf, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
1131c807f67SHans Petter Selasky
1141c807f67SHans Petter Selasky while (buf->load_done == MLX5_LOAD_ST_NONE)
1151c807f67SHans Petter Selasky MLX5_DMA_WAIT(dev);
1161c807f67SHans Petter Selasky MLX5_DMA_UNLOCK(dev);
1171c807f67SHans Petter Selasky
1181c807f67SHans Petter Selasky /* check for error */
1191c807f67SHans Petter Selasky if (buf->load_done != MLX5_LOAD_ST_SUCCESS) {
1201c807f67SHans Petter Selasky err = -ENOMEM;
1211c807f67SHans Petter Selasky goto err_dma_load;
1221c807f67SHans Petter Selasky }
1231c807f67SHans Petter Selasky
1241c807f67SHans Petter Selasky /* clean memory */
1251c807f67SHans Petter Selasky memset(buf->direct.buf, 0, PAGE_SIZE * buf->npages);
1261c807f67SHans Petter Selasky
1271c807f67SHans Petter Selasky /* flush memory to RAM */
1281c807f67SHans Petter Selasky bus_dmamap_sync(buf->dev->cmd.dma_tag, buf->dma_map, BUS_DMASYNC_PREWRITE);
1291c807f67SHans Petter Selasky return (0);
1301c807f67SHans Petter Selasky
1311c807f67SHans Petter Selasky err_dma_load:
1321c807f67SHans Petter Selasky bus_dmamem_free(buf->dma_tag, buf->direct.buf, buf->dma_map);
1331c807f67SHans Petter Selasky err_dma_alloc:
1341c807f67SHans Petter Selasky bus_dma_tag_destroy(buf->dma_tag);
1351c807f67SHans Petter Selasky err_dma_tag:
1361c807f67SHans Petter Selasky kfree(buf->page_list);
1371c807f67SHans Petter Selasky return (err);
1381c807f67SHans Petter Selasky }
139dc7e38acSHans Petter Selasky
mlx5_buf_free(struct mlx5_core_dev * dev,struct mlx5_buf * buf)140dc7e38acSHans Petter Selasky void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf)
141dc7e38acSHans Petter Selasky {
142dc7e38acSHans Petter Selasky
1431c807f67SHans Petter Selasky bus_dmamap_unload(buf->dma_tag, buf->dma_map);
1441c807f67SHans Petter Selasky bus_dmamem_free(buf->dma_tag, buf->direct.buf, buf->dma_map);
1451c807f67SHans Petter Selasky bus_dma_tag_destroy(buf->dma_tag);
146dc7e38acSHans Petter Selasky kfree(buf->page_list);
147dc7e38acSHans Petter Selasky }
148dc7e38acSHans Petter Selasky EXPORT_SYMBOL_GPL(mlx5_buf_free);
149dc7e38acSHans Petter Selasky
mlx5_alloc_db_pgdir(struct mlx5_core_dev * dev)1507c3eff94SHans Petter Selasky static struct mlx5_db_pgdir *mlx5_alloc_db_pgdir(struct mlx5_core_dev *dev)
151dc7e38acSHans Petter Selasky {
152dc7e38acSHans Petter Selasky struct mlx5_db_pgdir *pgdir;
153dc7e38acSHans Petter Selasky
154dc7e38acSHans Petter Selasky pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);
155dc7e38acSHans Petter Selasky
156dc7e38acSHans Petter Selasky bitmap_fill(pgdir->bitmap, MLX5_DB_PER_PAGE);
157dc7e38acSHans Petter Selasky
1581c807f67SHans Petter Selasky pgdir->fw_page = mlx5_fwp_alloc(dev, GFP_KERNEL, 1);
1591c807f67SHans Petter Selasky if (pgdir->fw_page != NULL) {
1601c807f67SHans Petter Selasky pgdir->db_page = pgdir->fw_page->virt_addr;
1611c807f67SHans Petter Selasky pgdir->db_dma = pgdir->fw_page->dma_addr;
1621c807f67SHans Petter Selasky
1631c807f67SHans Petter Selasky /* clean allocated memory */
1641c807f67SHans Petter Selasky memset(pgdir->db_page, 0, MLX5_ADAPTER_PAGE_SIZE);
1651c807f67SHans Petter Selasky
1661c807f67SHans Petter Selasky /* flush memory to RAM */
1671c807f67SHans Petter Selasky mlx5_fwp_flush(pgdir->fw_page);
1681c807f67SHans Petter Selasky }
169dc7e38acSHans Petter Selasky if (!pgdir->db_page) {
170dc7e38acSHans Petter Selasky kfree(pgdir);
171dc7e38acSHans Petter Selasky return NULL;
172dc7e38acSHans Petter Selasky }
173dc7e38acSHans Petter Selasky
174dc7e38acSHans Petter Selasky return pgdir;
175dc7e38acSHans Petter Selasky }
176dc7e38acSHans Petter Selasky
mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir * pgdir,struct mlx5_db * db)177dc7e38acSHans Petter Selasky static int mlx5_alloc_db_from_pgdir(struct mlx5_db_pgdir *pgdir,
178dc7e38acSHans Petter Selasky struct mlx5_db *db)
179dc7e38acSHans Petter Selasky {
180dc7e38acSHans Petter Selasky int offset;
181dc7e38acSHans Petter Selasky int i;
182dc7e38acSHans Petter Selasky
183dc7e38acSHans Petter Selasky i = find_first_bit(pgdir->bitmap, MLX5_DB_PER_PAGE);
184dc7e38acSHans Petter Selasky if (i >= MLX5_DB_PER_PAGE)
185dc7e38acSHans Petter Selasky return -ENOMEM;
186dc7e38acSHans Petter Selasky
187dc7e38acSHans Petter Selasky __clear_bit(i, pgdir->bitmap);
188dc7e38acSHans Petter Selasky
189dc7e38acSHans Petter Selasky db->u.pgdir = pgdir;
190dc7e38acSHans Petter Selasky db->index = i;
191dc7e38acSHans Petter Selasky offset = db->index * L1_CACHE_BYTES;
192dc7e38acSHans Petter Selasky db->db = pgdir->db_page + offset / sizeof(*pgdir->db_page);
193dc7e38acSHans Petter Selasky db->dma = pgdir->db_dma + offset;
194dc7e38acSHans Petter Selasky
195dc7e38acSHans Petter Selasky db->db[0] = 0;
196dc7e38acSHans Petter Selasky db->db[1] = 0;
197dc7e38acSHans Petter Selasky
198dc7e38acSHans Petter Selasky return 0;
199dc7e38acSHans Petter Selasky }
200dc7e38acSHans Petter Selasky
mlx5_db_alloc(struct mlx5_core_dev * dev,struct mlx5_db * db)2017c3eff94SHans Petter Selasky int mlx5_db_alloc(struct mlx5_core_dev *dev, struct mlx5_db *db)
202dc7e38acSHans Petter Selasky {
203dc7e38acSHans Petter Selasky struct mlx5_db_pgdir *pgdir;
204dc7e38acSHans Petter Selasky int ret = 0;
205dc7e38acSHans Petter Selasky
206dc7e38acSHans Petter Selasky mutex_lock(&dev->priv.pgdir_mutex);
207dc7e38acSHans Petter Selasky
208dc7e38acSHans Petter Selasky list_for_each_entry(pgdir, &dev->priv.pgdir_list, list)
209dc7e38acSHans Petter Selasky if (!mlx5_alloc_db_from_pgdir(pgdir, db))
210dc7e38acSHans Petter Selasky goto out;
211dc7e38acSHans Petter Selasky
2127c3eff94SHans Petter Selasky pgdir = mlx5_alloc_db_pgdir(dev);
213dc7e38acSHans Petter Selasky if (!pgdir) {
214dc7e38acSHans Petter Selasky ret = -ENOMEM;
215dc7e38acSHans Petter Selasky goto out;
216dc7e38acSHans Petter Selasky }
217dc7e38acSHans Petter Selasky
218dc7e38acSHans Petter Selasky list_add(&pgdir->list, &dev->priv.pgdir_list);
219dc7e38acSHans Petter Selasky
220dc7e38acSHans Petter Selasky /* This should never fail -- we just allocated an empty page: */
221dc7e38acSHans Petter Selasky WARN_ON(mlx5_alloc_db_from_pgdir(pgdir, db));
222dc7e38acSHans Petter Selasky
223dc7e38acSHans Petter Selasky out:
224dc7e38acSHans Petter Selasky mutex_unlock(&dev->priv.pgdir_mutex);
225dc7e38acSHans Petter Selasky
226dc7e38acSHans Petter Selasky return ret;
227dc7e38acSHans Petter Selasky }
228dc7e38acSHans Petter Selasky EXPORT_SYMBOL_GPL(mlx5_db_alloc);
229dc7e38acSHans Petter Selasky
mlx5_db_free(struct mlx5_core_dev * dev,struct mlx5_db * db)230dc7e38acSHans Petter Selasky void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db)
231dc7e38acSHans Petter Selasky {
232dc7e38acSHans Petter Selasky mutex_lock(&dev->priv.pgdir_mutex);
233dc7e38acSHans Petter Selasky
234dc7e38acSHans Petter Selasky __set_bit(db->index, db->u.pgdir->bitmap);
235dc7e38acSHans Petter Selasky
236dc7e38acSHans Petter Selasky if (bitmap_full(db->u.pgdir->bitmap, MLX5_DB_PER_PAGE)) {
2371c807f67SHans Petter Selasky mlx5_fwp_free(db->u.pgdir->fw_page);
238dc7e38acSHans Petter Selasky list_del(&db->u.pgdir->list);
239dc7e38acSHans Petter Selasky kfree(db->u.pgdir);
240dc7e38acSHans Petter Selasky }
241dc7e38acSHans Petter Selasky
242dc7e38acSHans Petter Selasky mutex_unlock(&dev->priv.pgdir_mutex);
243dc7e38acSHans Petter Selasky }
244dc7e38acSHans Petter Selasky EXPORT_SYMBOL_GPL(mlx5_db_free);
245dc7e38acSHans Petter Selasky
2461c807f67SHans Petter Selasky void
mlx5_fill_page_array(struct mlx5_buf * buf,__be64 * pas)2471c807f67SHans Petter Selasky mlx5_fill_page_array(struct mlx5_buf *buf, __be64 *pas)
248dc7e38acSHans Petter Selasky {
249dc7e38acSHans Petter Selasky int i;
250dc7e38acSHans Petter Selasky
2511c807f67SHans Petter Selasky for (i = 0; i != buf->npages; i++)
2521c807f67SHans Petter Selasky pas[i] = cpu_to_be64(buf->page_list[i]);
253dc7e38acSHans Petter Selasky }
254dc7e38acSHans Petter Selasky EXPORT_SYMBOL_GPL(mlx5_fill_page_array);
255