11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * Copyright (c) 2004 Topspin Communications. All rights reserved.
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * This software is available to you under a choice of one of two
51da177e4SLinus Torvalds * licenses. You may choose to be licensed under the terms of the GNU
61da177e4SLinus Torvalds * General Public License (GPL) Version 2, available from the file
71da177e4SLinus Torvalds * COPYING in the main directory of this source tree, or the
81da177e4SLinus Torvalds * OpenIB.org BSD license below:
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or
111da177e4SLinus Torvalds * without modification, are permitted provided that the following
121da177e4SLinus Torvalds * conditions are met:
131da177e4SLinus Torvalds *
141da177e4SLinus Torvalds * - Redistributions of source code must retain the above
151da177e4SLinus Torvalds * copyright notice, this list of conditions and the following
161da177e4SLinus Torvalds * disclaimer.
171da177e4SLinus Torvalds *
181da177e4SLinus Torvalds * - Redistributions in binary form must reproduce the above
191da177e4SLinus Torvalds * copyright notice, this list of conditions and the following
201da177e4SLinus Torvalds * disclaimer in the documentation and/or other materials
211da177e4SLinus Torvalds * provided with the distribution.
221da177e4SLinus Torvalds *
231da177e4SLinus Torvalds * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
241da177e4SLinus Torvalds * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
251da177e4SLinus Torvalds * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
261da177e4SLinus Torvalds * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
271da177e4SLinus Torvalds * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
281da177e4SLinus Torvalds * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
291da177e4SLinus Torvalds * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
301da177e4SLinus Torvalds * SOFTWARE.
311da177e4SLinus Torvalds */
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds #include <linux/errno.h>
341da177e4SLinus Torvalds #include <linux/slab.h>
351da177e4SLinus Torvalds #include <linux/bitmap.h>
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds #include "mthca_dev.h"
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds /* Trivial bitmap-based allocator */
mthca_alloc(struct mthca_alloc * alloc)401da177e4SLinus Torvalds u32 mthca_alloc(struct mthca_alloc *alloc)
411da177e4SLinus Torvalds {
425a4e6dccSRoland Dreier unsigned long flags;
431da177e4SLinus Torvalds u32 obj;
441da177e4SLinus Torvalds
455a4e6dccSRoland Dreier spin_lock_irqsave(&alloc->lock, flags);
465a4e6dccSRoland Dreier
471da177e4SLinus Torvalds obj = find_next_zero_bit(alloc->table, alloc->max, alloc->last);
481da177e4SLinus Torvalds if (obj >= alloc->max) {
491da177e4SLinus Torvalds alloc->top = (alloc->top + alloc->max) & alloc->mask;
501da177e4SLinus Torvalds obj = find_first_zero_bit(alloc->table, alloc->max);
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds
531da177e4SLinus Torvalds if (obj < alloc->max) {
54*19453f34SChristophe JAILLET __set_bit(obj, alloc->table);
551da177e4SLinus Torvalds obj |= alloc->top;
561da177e4SLinus Torvalds } else
571da177e4SLinus Torvalds obj = -1;
581da177e4SLinus Torvalds
595a4e6dccSRoland Dreier spin_unlock_irqrestore(&alloc->lock, flags);
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds return obj;
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds
mthca_free(struct mthca_alloc * alloc,u32 obj)641da177e4SLinus Torvalds void mthca_free(struct mthca_alloc *alloc, u32 obj)
651da177e4SLinus Torvalds {
665a4e6dccSRoland Dreier unsigned long flags;
675a4e6dccSRoland Dreier
681da177e4SLinus Torvalds obj &= alloc->max - 1;
695a4e6dccSRoland Dreier
705a4e6dccSRoland Dreier spin_lock_irqsave(&alloc->lock, flags);
715a4e6dccSRoland Dreier
72*19453f34SChristophe JAILLET __clear_bit(obj, alloc->table);
731da177e4SLinus Torvalds alloc->last = min(alloc->last, obj);
741da177e4SLinus Torvalds alloc->top = (alloc->top + alloc->max) & alloc->mask;
755a4e6dccSRoland Dreier
765a4e6dccSRoland Dreier spin_unlock_irqrestore(&alloc->lock, flags);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds
mthca_alloc_init(struct mthca_alloc * alloc,u32 num,u32 mask,u32 reserved)791da177e4SLinus Torvalds int mthca_alloc_init(struct mthca_alloc *alloc, u32 num, u32 mask,
801da177e4SLinus Torvalds u32 reserved)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds /* num must be a power of 2 */
831da177e4SLinus Torvalds if (num != 1 << (ffs(num) - 1))
841da177e4SLinus Torvalds return -EINVAL;
851da177e4SLinus Torvalds
861da177e4SLinus Torvalds alloc->last = 0;
871da177e4SLinus Torvalds alloc->top = 0;
881da177e4SLinus Torvalds alloc->max = num;
891da177e4SLinus Torvalds alloc->mask = mask;
901da177e4SLinus Torvalds spin_lock_init(&alloc->lock);
9112d1e2f3SChristophe JAILLET alloc->table = bitmap_zalloc(num, GFP_KERNEL);
921da177e4SLinus Torvalds if (!alloc->table)
931da177e4SLinus Torvalds return -ENOMEM;
941da177e4SLinus Torvalds
95a277f383SChristophe JAILLET bitmap_set(alloc->table, 0, reserved);
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds return 0;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
mthca_alloc_cleanup(struct mthca_alloc * alloc)1001da177e4SLinus Torvalds void mthca_alloc_cleanup(struct mthca_alloc *alloc)
1011da177e4SLinus Torvalds {
10212d1e2f3SChristophe JAILLET bitmap_free(alloc->table);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds /*
1061da177e4SLinus Torvalds * Array of pointers with lazy allocation of leaf pages. Callers of
1071da177e4SLinus Torvalds * _get, _set and _clear methods must use a lock or otherwise
1081da177e4SLinus Torvalds * serialize access to the array.
1091da177e4SLinus Torvalds */
1101da177e4SLinus Torvalds
11169e9fbb4SRoland Dreier #define MTHCA_ARRAY_MASK (PAGE_SIZE / sizeof (void *) - 1)
11269e9fbb4SRoland Dreier
mthca_array_get(struct mthca_array * array,int index)1131da177e4SLinus Torvalds void *mthca_array_get(struct mthca_array *array, int index)
1141da177e4SLinus Torvalds {
1151da177e4SLinus Torvalds int p = (index * sizeof (void *)) >> PAGE_SHIFT;
1161da177e4SLinus Torvalds
11769e9fbb4SRoland Dreier if (array->page_list[p].page)
11869e9fbb4SRoland Dreier return array->page_list[p].page[index & MTHCA_ARRAY_MASK];
11969e9fbb4SRoland Dreier else
1201da177e4SLinus Torvalds return NULL;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds
mthca_array_set(struct mthca_array * array,int index,void * value)1231da177e4SLinus Torvalds int mthca_array_set(struct mthca_array *array, int index, void *value)
1241da177e4SLinus Torvalds {
1251da177e4SLinus Torvalds int p = (index * sizeof (void *)) >> PAGE_SHIFT;
1261da177e4SLinus Torvalds
1271da177e4SLinus Torvalds /* Allocate with GFP_ATOMIC because we'll be called with locks held. */
1281da177e4SLinus Torvalds if (!array->page_list[p].page)
1291da177e4SLinus Torvalds array->page_list[p].page = (void **) get_zeroed_page(GFP_ATOMIC);
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds if (!array->page_list[p].page)
1321da177e4SLinus Torvalds return -ENOMEM;
1331da177e4SLinus Torvalds
13469e9fbb4SRoland Dreier array->page_list[p].page[index & MTHCA_ARRAY_MASK] = value;
1351da177e4SLinus Torvalds ++array->page_list[p].used;
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds
mthca_array_clear(struct mthca_array * array,int index)1401da177e4SLinus Torvalds void mthca_array_clear(struct mthca_array *array, int index)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds int p = (index * sizeof (void *)) >> PAGE_SHIFT;
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds if (--array->page_list[p].used == 0) {
1451da177e4SLinus Torvalds free_page((unsigned long) array->page_list[p].page);
1461da177e4SLinus Torvalds array->page_list[p].page = NULL;
147bf74c747SMichael S. Tsirkin } else
14869e9fbb4SRoland Dreier array->page_list[p].page[index & MTHCA_ARRAY_MASK] = NULL;
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds if (array->page_list[p].used < 0)
1511da177e4SLinus Torvalds pr_debug("Array %p index %d page %d with ref count %d < 0\n",
1521da177e4SLinus Torvalds array, index, p, array->page_list[p].used);
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds
mthca_array_init(struct mthca_array * array,int nent)1551da177e4SLinus Torvalds int mthca_array_init(struct mthca_array *array, int nent)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds int npage = (nent * sizeof (void *) + PAGE_SIZE - 1) / PAGE_SIZE;
1581da177e4SLinus Torvalds int i;
1591da177e4SLinus Torvalds
1606da2ec56SKees Cook array->page_list = kmalloc_array(npage, sizeof(*array->page_list),
1616da2ec56SKees Cook GFP_KERNEL);
1621da177e4SLinus Torvalds if (!array->page_list)
1631da177e4SLinus Torvalds return -ENOMEM;
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds for (i = 0; i < npage; ++i) {
1661da177e4SLinus Torvalds array->page_list[i].page = NULL;
1671da177e4SLinus Torvalds array->page_list[i].used = 0;
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds return 0;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds
mthca_array_cleanup(struct mthca_array * array,int nent)1731da177e4SLinus Torvalds void mthca_array_cleanup(struct mthca_array *array, int nent)
1741da177e4SLinus Torvalds {
1751da177e4SLinus Torvalds int i;
1761da177e4SLinus Torvalds
1771da177e4SLinus Torvalds for (i = 0; i < (nent * sizeof (void *) + PAGE_SIZE - 1) / PAGE_SIZE; ++i)
1781da177e4SLinus Torvalds free_page((unsigned long) array->page_list[i].page);
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds kfree(array->page_list);
1811da177e4SLinus Torvalds }
18287b81670SRoland Dreier
18387b81670SRoland Dreier /*
18487b81670SRoland Dreier * Handling for queue buffers -- we allocate a bunch of memory and
18587b81670SRoland Dreier * register it in a memory region at HCA virtual address 0. If the
18687b81670SRoland Dreier * requested size is > max_direct, we split the allocation into
18787b81670SRoland Dreier * multiple pages, so we don't require too much contiguous memory.
18887b81670SRoland Dreier */
18987b81670SRoland Dreier
mthca_buf_alloc(struct mthca_dev * dev,int size,int max_direct,union mthca_buf * buf,int * is_direct,struct mthca_pd * pd,int hca_write,struct mthca_mr * mr)19087b81670SRoland Dreier int mthca_buf_alloc(struct mthca_dev *dev, int size, int max_direct,
19187b81670SRoland Dreier union mthca_buf *buf, int *is_direct, struct mthca_pd *pd,
19287b81670SRoland Dreier int hca_write, struct mthca_mr *mr)
19387b81670SRoland Dreier {
19487b81670SRoland Dreier int err = -ENOMEM;
19587b81670SRoland Dreier int npages, shift;
19687b81670SRoland Dreier u64 *dma_list = NULL;
19787b81670SRoland Dreier dma_addr_t t;
19887b81670SRoland Dreier int i;
19987b81670SRoland Dreier
20087b81670SRoland Dreier if (size <= max_direct) {
20187b81670SRoland Dreier *is_direct = 1;
20287b81670SRoland Dreier npages = 1;
20387b81670SRoland Dreier shift = get_order(size) + PAGE_SHIFT;
20487b81670SRoland Dreier
20587b81670SRoland Dreier buf->direct.buf = dma_alloc_coherent(&dev->pdev->dev,
20687b81670SRoland Dreier size, &t, GFP_KERNEL);
20787b81670SRoland Dreier if (!buf->direct.buf)
20887b81670SRoland Dreier return -ENOMEM;
20987b81670SRoland Dreier
2103a2baff7SFUJITA Tomonori dma_unmap_addr_set(&buf->direct, mapping, t);
21187b81670SRoland Dreier
21287b81670SRoland Dreier while (t & ((1 << shift) - 1)) {
21387b81670SRoland Dreier --shift;
21487b81670SRoland Dreier npages *= 2;
21587b81670SRoland Dreier }
21687b81670SRoland Dreier
2176da2ec56SKees Cook dma_list = kmalloc_array(npages, sizeof(*dma_list),
2186da2ec56SKees Cook GFP_KERNEL);
21987b81670SRoland Dreier if (!dma_list)
22087b81670SRoland Dreier goto err_free;
22187b81670SRoland Dreier
22287b81670SRoland Dreier for (i = 0; i < npages; ++i)
22387b81670SRoland Dreier dma_list[i] = t + i * (1 << shift);
22487b81670SRoland Dreier } else {
22587b81670SRoland Dreier *is_direct = 0;
22687b81670SRoland Dreier npages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
22787b81670SRoland Dreier shift = PAGE_SHIFT;
22887b81670SRoland Dreier
2296da2ec56SKees Cook dma_list = kmalloc_array(npages, sizeof(*dma_list),
2306da2ec56SKees Cook GFP_KERNEL);
23187b81670SRoland Dreier if (!dma_list)
23287b81670SRoland Dreier return -ENOMEM;
23387b81670SRoland Dreier
2346da2ec56SKees Cook buf->page_list = kmalloc_array(npages,
2356da2ec56SKees Cook sizeof(*buf->page_list),
23687b81670SRoland Dreier GFP_KERNEL);
23787b81670SRoland Dreier if (!buf->page_list)
23887b81670SRoland Dreier goto err_out;
23987b81670SRoland Dreier
24087b81670SRoland Dreier for (i = 0; i < npages; ++i)
24187b81670SRoland Dreier buf->page_list[i].buf = NULL;
24287b81670SRoland Dreier
24387b81670SRoland Dreier for (i = 0; i < npages; ++i) {
24487b81670SRoland Dreier buf->page_list[i].buf =
24587b81670SRoland Dreier dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE,
24687b81670SRoland Dreier &t, GFP_KERNEL);
24787b81670SRoland Dreier if (!buf->page_list[i].buf)
24887b81670SRoland Dreier goto err_free;
24987b81670SRoland Dreier
25087b81670SRoland Dreier dma_list[i] = t;
2513a2baff7SFUJITA Tomonori dma_unmap_addr_set(&buf->page_list[i], mapping, t);
25287b81670SRoland Dreier
2538909c571SShani Moideen clear_page(buf->page_list[i].buf);
25487b81670SRoland Dreier }
25587b81670SRoland Dreier }
25687b81670SRoland Dreier
25787b81670SRoland Dreier err = mthca_mr_alloc_phys(dev, pd->pd_num,
25887b81670SRoland Dreier dma_list, shift, npages,
25987b81670SRoland Dreier 0, size,
26087b81670SRoland Dreier MTHCA_MPT_FLAG_LOCAL_READ |
26187b81670SRoland Dreier (hca_write ? MTHCA_MPT_FLAG_LOCAL_WRITE : 0),
26287b81670SRoland Dreier mr);
26387b81670SRoland Dreier if (err)
26487b81670SRoland Dreier goto err_free;
26587b81670SRoland Dreier
26687b81670SRoland Dreier kfree(dma_list);
26787b81670SRoland Dreier
26887b81670SRoland Dreier return 0;
26987b81670SRoland Dreier
27087b81670SRoland Dreier err_free:
27187b81670SRoland Dreier mthca_buf_free(dev, size, buf, *is_direct, NULL);
27287b81670SRoland Dreier
27387b81670SRoland Dreier err_out:
27487b81670SRoland Dreier kfree(dma_list);
27587b81670SRoland Dreier
27687b81670SRoland Dreier return err;
27787b81670SRoland Dreier }
27887b81670SRoland Dreier
mthca_buf_free(struct mthca_dev * dev,int size,union mthca_buf * buf,int is_direct,struct mthca_mr * mr)27987b81670SRoland Dreier void mthca_buf_free(struct mthca_dev *dev, int size, union mthca_buf *buf,
28087b81670SRoland Dreier int is_direct, struct mthca_mr *mr)
28187b81670SRoland Dreier {
28287b81670SRoland Dreier int i;
28387b81670SRoland Dreier
28487b81670SRoland Dreier if (mr)
28587b81670SRoland Dreier mthca_free_mr(dev, mr);
28687b81670SRoland Dreier
28787b81670SRoland Dreier if (is_direct)
28887b81670SRoland Dreier dma_free_coherent(&dev->pdev->dev, size, buf->direct.buf,
2893a2baff7SFUJITA Tomonori dma_unmap_addr(&buf->direct, mapping));
29087b81670SRoland Dreier else {
29187b81670SRoland Dreier for (i = 0; i < (size + PAGE_SIZE - 1) / PAGE_SIZE; ++i)
29287b81670SRoland Dreier dma_free_coherent(&dev->pdev->dev, PAGE_SIZE,
29387b81670SRoland Dreier buf->page_list[i].buf,
2943a2baff7SFUJITA Tomonori dma_unmap_addr(&buf->page_list[i],
29587b81670SRoland Dreier mapping));
29687b81670SRoland Dreier kfree(buf->page_list);
29787b81670SRoland Dreier }
29887b81670SRoland Dreier }
299