brintos

brintos / linux-shallow public Read only

0
0
Text · 18.3 KiB · b330020 Raw
819 lines · c
1/*2 * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.3 * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.4 *5 * This software is available to you under a choice of one of two6 * licenses.  You may choose to be licensed under the terms of the GNU7 * General Public License (GPL) Version 2, available from the file8 * COPYING in the main directory of this source tree, or the9 * OpenIB.org BSD license below:10 *11 *     Redistribution and use in source and binary forms, with or12 *     without modification, are permitted provided that the following13 *     conditions are met:14 *15 *      - Redistributions of source code must retain the above16 *        copyright notice, this list of conditions and the following17 *        disclaimer.18 *19 *      - Redistributions in binary form must reproduce the above20 *        copyright notice, this list of conditions and the following21 *        disclaimer in the documentation and/or other materials22 *        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 OF26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE31 * SOFTWARE.32 */33 34#include <linux/errno.h>35#include <linux/slab.h>36#include <linux/mm.h>37#include <linux/export.h>38#include <linux/bitmap.h>39#include <linux/dma-mapping.h>40#include <linux/vmalloc.h>41 42#include "mlx4.h"43 44u32 mlx4_bitmap_alloc(struct mlx4_bitmap *bitmap)45{46	u32 obj;47 48	spin_lock(&bitmap->lock);49 50	obj = find_next_zero_bit(bitmap->table, bitmap->max, bitmap->last);51	if (obj >= bitmap->max) {52		bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top)53				& bitmap->mask;54		obj = find_first_zero_bit(bitmap->table, bitmap->max);55	}56 57	if (obj < bitmap->max) {58		set_bit(obj, bitmap->table);59		bitmap->last = (obj + 1);60		if (bitmap->last == bitmap->max)61			bitmap->last = 0;62		obj |= bitmap->top;63	} else64		obj = -1;65 66	if (obj != -1)67		--bitmap->avail;68 69	spin_unlock(&bitmap->lock);70 71	return obj;72}73 74void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj, int use_rr)75{76	mlx4_bitmap_free_range(bitmap, obj, 1, use_rr);77}78 79static unsigned long find_aligned_range(unsigned long *bitmap,80					u32 start, u32 nbits,81					int len, int align, u32 skip_mask)82{83	unsigned long end, i;84 85again:86	start = ALIGN(start, align);87 88	while ((start < nbits) && (test_bit(start, bitmap) ||89				   (start & skip_mask)))90		start += align;91 92	if (start >= nbits)93		return -1;94 95	end = start+len;96	if (end > nbits)97		return -1;98 99	for (i = start + 1; i < end; i++) {100		if (test_bit(i, bitmap) || ((u32)i & skip_mask)) {101			start = i + 1;102			goto again;103		}104	}105 106	return start;107}108 109u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt,110			    int align, u32 skip_mask)111{112	u32 obj;113 114	if (likely(cnt == 1 && align == 1 && !skip_mask))115		return mlx4_bitmap_alloc(bitmap);116 117	spin_lock(&bitmap->lock);118 119	obj = find_aligned_range(bitmap->table, bitmap->last,120				 bitmap->max, cnt, align, skip_mask);121	if (obj >= bitmap->max) {122		bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top)123				& bitmap->mask;124		obj = find_aligned_range(bitmap->table, 0, bitmap->max,125					 cnt, align, skip_mask);126	}127 128	if (obj < bitmap->max) {129		bitmap_set(bitmap->table, obj, cnt);130		if (obj == bitmap->last) {131			bitmap->last = (obj + cnt);132			if (bitmap->last >= bitmap->max)133				bitmap->last = 0;134		}135		obj |= bitmap->top;136	} else137		obj = -1;138 139	if (obj != -1)140		bitmap->avail -= cnt;141 142	spin_unlock(&bitmap->lock);143 144	return obj;145}146 147u32 mlx4_bitmap_avail(struct mlx4_bitmap *bitmap)148{149	return bitmap->avail;150}151 152static u32 mlx4_bitmap_masked_value(struct mlx4_bitmap *bitmap, u32 obj)153{154	return obj & (bitmap->max + bitmap->reserved_top - 1);155}156 157void mlx4_bitmap_free_range(struct mlx4_bitmap *bitmap, u32 obj, int cnt,158			    int use_rr)159{160	obj &= bitmap->max + bitmap->reserved_top - 1;161 162	spin_lock(&bitmap->lock);163	if (!use_rr) {164		bitmap->last = min(bitmap->last, obj);165		bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top)166				& bitmap->mask;167	}168	bitmap_clear(bitmap->table, obj, cnt);169	bitmap->avail += cnt;170	spin_unlock(&bitmap->lock);171}172 173int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask,174		     u32 reserved_bot, u32 reserved_top)175{176	/* num must be a power of 2 */177	if (num != roundup_pow_of_two(num))178		return -EINVAL;179 180	bitmap->last = 0;181	bitmap->top  = 0;182	bitmap->max  = num - reserved_top;183	bitmap->mask = mask;184	bitmap->reserved_top = reserved_top;185	bitmap->avail = num - reserved_top - reserved_bot;186	bitmap->effective_len = bitmap->avail;187	spin_lock_init(&bitmap->lock);188	bitmap->table = bitmap_zalloc(bitmap->max, GFP_KERNEL);189	if (!bitmap->table)190		return -ENOMEM;191 192	bitmap_set(bitmap->table, 0, reserved_bot);193 194	return 0;195}196 197void mlx4_bitmap_cleanup(struct mlx4_bitmap *bitmap)198{199	bitmap_free(bitmap->table);200}201 202struct mlx4_zone_allocator {203	struct list_head		entries;204	struct list_head		prios;205	u32				last_uid;206	u32				mask;207	/* protect the zone_allocator from concurrent accesses */208	spinlock_t			lock;209	enum mlx4_zone_alloc_flags	flags;210};211 212struct mlx4_zone_entry {213	struct list_head		list;214	struct list_head		prio_list;215	u32				uid;216	struct mlx4_zone_allocator	*allocator;217	struct mlx4_bitmap		*bitmap;218	int				use_rr;219	int				priority;220	int				offset;221	enum mlx4_zone_flags		flags;222};223 224struct mlx4_zone_allocator *mlx4_zone_allocator_create(enum mlx4_zone_alloc_flags flags)225{226	struct mlx4_zone_allocator *zones = kmalloc(sizeof(*zones), GFP_KERNEL);227 228	if (NULL == zones)229		return NULL;230 231	INIT_LIST_HEAD(&zones->entries);232	INIT_LIST_HEAD(&zones->prios);233	spin_lock_init(&zones->lock);234	zones->last_uid = 0;235	zones->mask = 0;236	zones->flags = flags;237 238	return zones;239}240 241int mlx4_zone_add_one(struct mlx4_zone_allocator *zone_alloc,242		      struct mlx4_bitmap *bitmap,243		      u32 flags,244		      int priority,245		      int offset,246		      u32 *puid)247{248	u32 mask = mlx4_bitmap_masked_value(bitmap, (u32)-1);249	struct mlx4_zone_entry *it;250	struct mlx4_zone_entry *zone = kmalloc(sizeof(*zone), GFP_KERNEL);251 252	if (NULL == zone)253		return -ENOMEM;254 255	zone->flags = flags;256	zone->bitmap = bitmap;257	zone->use_rr = (flags & MLX4_ZONE_USE_RR) ? MLX4_USE_RR : 0;258	zone->priority = priority;259	zone->offset = offset;260 261	spin_lock(&zone_alloc->lock);262 263	zone->uid = zone_alloc->last_uid++;264	zone->allocator = zone_alloc;265 266	if (zone_alloc->mask < mask)267		zone_alloc->mask = mask;268 269	list_for_each_entry(it, &zone_alloc->prios, prio_list)270		if (it->priority >= priority)271			break;272 273	if (&it->prio_list == &zone_alloc->prios || it->priority > priority)274		list_add_tail(&zone->prio_list, &it->prio_list);275	list_add_tail(&zone->list, &it->list);276 277	spin_unlock(&zone_alloc->lock);278 279	*puid = zone->uid;280 281	return 0;282}283 284/* Should be called under a lock */285static void __mlx4_zone_remove_one_entry(struct mlx4_zone_entry *entry)286{287	struct mlx4_zone_allocator *zone_alloc = entry->allocator;288 289	if (!list_empty(&entry->prio_list)) {290		/* Check if we need to add an alternative node to the prio list */291		if (!list_is_last(&entry->list, &zone_alloc->entries)) {292			struct mlx4_zone_entry *next = list_first_entry(&entry->list,293									typeof(*next),294									list);295 296			if (next->priority == entry->priority)297				list_add_tail(&next->prio_list, &entry->prio_list);298		}299 300		list_del(&entry->prio_list);301	}302 303	list_del(&entry->list);304 305	if (zone_alloc->flags & MLX4_ZONE_ALLOC_FLAGS_NO_OVERLAP) {306		u32 mask = 0;307		struct mlx4_zone_entry *it;308 309		list_for_each_entry(it, &zone_alloc->prios, prio_list) {310			u32 cur_mask = mlx4_bitmap_masked_value(it->bitmap, (u32)-1);311 312			if (mask < cur_mask)313				mask = cur_mask;314		}315		zone_alloc->mask = mask;316	}317}318 319void mlx4_zone_allocator_destroy(struct mlx4_zone_allocator *zone_alloc)320{321	struct mlx4_zone_entry *zone, *tmp;322 323	spin_lock(&zone_alloc->lock);324 325	list_for_each_entry_safe(zone, tmp, &zone_alloc->entries, list) {326		list_del(&zone->list);327		list_del(&zone->prio_list);328		kfree(zone);329	}330 331	spin_unlock(&zone_alloc->lock);332	kfree(zone_alloc);333}334 335/* Should be called under a lock */336static u32 __mlx4_alloc_from_zone(struct mlx4_zone_entry *zone, int count,337				  int align, u32 skip_mask, u32 *puid)338{339	u32 uid = 0;340	u32 res;341	struct mlx4_zone_allocator *zone_alloc = zone->allocator;342	struct mlx4_zone_entry *curr_node;343 344	res = mlx4_bitmap_alloc_range(zone->bitmap, count,345				      align, skip_mask);346 347	if (res != (u32)-1) {348		res += zone->offset;349		uid = zone->uid;350		goto out;351	}352 353	list_for_each_entry(curr_node, &zone_alloc->prios, prio_list) {354		if (unlikely(curr_node->priority == zone->priority))355			break;356	}357 358	if (zone->flags & MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO) {359		struct mlx4_zone_entry *it = curr_node;360 361		list_for_each_entry_continue_reverse(it, &zone_alloc->entries, list) {362			res = mlx4_bitmap_alloc_range(it->bitmap, count,363						      align, skip_mask);364			if (res != (u32)-1) {365				res += it->offset;366				uid = it->uid;367				goto out;368			}369		}370	}371 372	if (zone->flags & MLX4_ZONE_ALLOW_ALLOC_FROM_EQ_PRIO) {373		struct mlx4_zone_entry *it = curr_node;374 375		list_for_each_entry_from(it, &zone_alloc->entries, list) {376			if (unlikely(it == zone))377				continue;378 379			if (unlikely(it->priority != curr_node->priority))380				break;381 382			res = mlx4_bitmap_alloc_range(it->bitmap, count,383						      align, skip_mask);384			if (res != (u32)-1) {385				res += it->offset;386				uid = it->uid;387				goto out;388			}389		}390	}391 392	if (zone->flags & MLX4_ZONE_FALLBACK_TO_HIGHER_PRIO) {393		if (list_is_last(&curr_node->prio_list, &zone_alloc->prios))394			goto out;395 396		curr_node = list_first_entry(&curr_node->prio_list,397					     typeof(*curr_node),398					     prio_list);399 400		list_for_each_entry_from(curr_node, &zone_alloc->entries, list) {401			res = mlx4_bitmap_alloc_range(curr_node->bitmap, count,402						      align, skip_mask);403			if (res != (u32)-1) {404				res += curr_node->offset;405				uid = curr_node->uid;406				goto out;407			}408		}409	}410 411out:412	if (NULL != puid && res != (u32)-1)413		*puid = uid;414	return res;415}416 417/* Should be called under a lock */418static void __mlx4_free_from_zone(struct mlx4_zone_entry *zone, u32 obj,419				  u32 count)420{421	mlx4_bitmap_free_range(zone->bitmap, obj - zone->offset, count, zone->use_rr);422}423 424/* Should be called under a lock */425static struct mlx4_zone_entry *__mlx4_find_zone_by_uid(426		struct mlx4_zone_allocator *zones, u32 uid)427{428	struct mlx4_zone_entry *zone;429 430	list_for_each_entry(zone, &zones->entries, list) {431		if (zone->uid == uid)432			return zone;433	}434 435	return NULL;436}437 438struct mlx4_bitmap *mlx4_zone_get_bitmap(struct mlx4_zone_allocator *zones, u32 uid)439{440	struct mlx4_zone_entry *zone;441	struct mlx4_bitmap *bitmap;442 443	spin_lock(&zones->lock);444 445	zone = __mlx4_find_zone_by_uid(zones, uid);446 447	bitmap = zone == NULL ? NULL : zone->bitmap;448 449	spin_unlock(&zones->lock);450 451	return bitmap;452}453 454int mlx4_zone_remove_one(struct mlx4_zone_allocator *zones, u32 uid)455{456	struct mlx4_zone_entry *zone;457	int res = 0;458 459	spin_lock(&zones->lock);460 461	zone = __mlx4_find_zone_by_uid(zones, uid);462 463	if (NULL == zone) {464		res = -1;465		goto out;466	}467 468	__mlx4_zone_remove_one_entry(zone);469 470out:471	spin_unlock(&zones->lock);472	kfree(zone);473 474	return res;475}476 477/* Should be called under a lock */478static struct mlx4_zone_entry *__mlx4_find_zone_by_uid_unique(479		struct mlx4_zone_allocator *zones, u32 obj)480{481	struct mlx4_zone_entry *zone, *zone_candidate = NULL;482	u32 dist = (u32)-1;483 484	/* Search for the smallest zone that this obj could be485	 * allocated from. This is done in order to handle486	 * situations when small bitmaps are allocated from bigger487	 * bitmaps (and the allocated space is marked as reserved in488	 * the bigger bitmap.489	 */490	list_for_each_entry(zone, &zones->entries, list) {491		if (obj >= zone->offset) {492			u32 mobj = (obj - zone->offset) & zones->mask;493 494			if (mobj < zone->bitmap->max) {495				u32 curr_dist = zone->bitmap->effective_len;496 497				if (curr_dist < dist) {498					dist = curr_dist;499					zone_candidate = zone;500				}501			}502		}503	}504 505	return zone_candidate;506}507 508u32 mlx4_zone_alloc_entries(struct mlx4_zone_allocator *zones, u32 uid, int count,509			    int align, u32 skip_mask, u32 *puid)510{511	struct mlx4_zone_entry *zone;512	int res = -1;513 514	spin_lock(&zones->lock);515 516	zone = __mlx4_find_zone_by_uid(zones, uid);517 518	if (NULL == zone)519		goto out;520 521	res = __mlx4_alloc_from_zone(zone, count, align, skip_mask, puid);522 523out:524	spin_unlock(&zones->lock);525 526	return res;527}528 529u32 mlx4_zone_free_entries(struct mlx4_zone_allocator *zones, u32 uid, u32 obj, u32 count)530{531	struct mlx4_zone_entry *zone;532	int res = 0;533 534	spin_lock(&zones->lock);535 536	zone = __mlx4_find_zone_by_uid(zones, uid);537 538	if (NULL == zone) {539		res = -1;540		goto out;541	}542 543	__mlx4_free_from_zone(zone, obj, count);544 545out:546	spin_unlock(&zones->lock);547 548	return res;549}550 551u32 mlx4_zone_free_entries_unique(struct mlx4_zone_allocator *zones, u32 obj, u32 count)552{553	struct mlx4_zone_entry *zone;554	int res;555 556	if (!(zones->flags & MLX4_ZONE_ALLOC_FLAGS_NO_OVERLAP))557		return -EFAULT;558 559	spin_lock(&zones->lock);560 561	zone = __mlx4_find_zone_by_uid_unique(zones, obj);562 563	if (NULL == zone) {564		res = -1;565		goto out;566	}567 568	__mlx4_free_from_zone(zone, obj, count);569	res = 0;570 571out:572	spin_unlock(&zones->lock);573 574	return res;575}576 577static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size,578				 struct mlx4_buf *buf)579{580	dma_addr_t t;581 582	buf->nbufs        = 1;583	buf->npages       = 1;584	buf->page_shift   = get_order(size) + PAGE_SHIFT;585	buf->direct.buf   =586		dma_alloc_coherent(&dev->persist->pdev->dev, size, &t,587				   GFP_KERNEL);588	if (!buf->direct.buf)589		return -ENOMEM;590 591	buf->direct.map = t;592 593	while (t & ((1 << buf->page_shift) - 1)) {594		--buf->page_shift;595		buf->npages *= 2;596	}597 598	return 0;599}600 601/* Handling for queue buffers -- we allocate a bunch of memory and602 * register it in a memory region at HCA virtual address 0. If the603 *  requested size is > max_direct, we split the allocation into604 *  multiple pages, so we don't require too much contiguous memory.605 */606int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct,607		   struct mlx4_buf *buf)608{609	if (size <= max_direct) {610		return mlx4_buf_direct_alloc(dev, size, buf);611	} else {612		dma_addr_t t;613		int i;614 615		buf->direct.buf = NULL;616		buf->nbufs      = DIV_ROUND_UP(size, PAGE_SIZE);617		buf->npages	= buf->nbufs;618		buf->page_shift  = PAGE_SHIFT;619		buf->page_list   = kcalloc(buf->nbufs, sizeof(*buf->page_list),620					   GFP_KERNEL);621		if (!buf->page_list)622			return -ENOMEM;623 624		for (i = 0; i < buf->nbufs; ++i) {625			buf->page_list[i].buf =626				dma_alloc_coherent(&dev->persist->pdev->dev,627						   PAGE_SIZE, &t, GFP_KERNEL);628			if (!buf->page_list[i].buf)629				goto err_free;630 631			buf->page_list[i].map = t;632		}633	}634 635	return 0;636 637err_free:638	mlx4_buf_free(dev, size, buf);639 640	return -ENOMEM;641}642EXPORT_SYMBOL_GPL(mlx4_buf_alloc);643 644void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf)645{646	if (buf->nbufs == 1) {647		dma_free_coherent(&dev->persist->pdev->dev, size,648				  buf->direct.buf, buf->direct.map);649	} else {650		int i;651 652		for (i = 0; i < buf->nbufs; ++i)653			if (buf->page_list[i].buf)654				dma_free_coherent(&dev->persist->pdev->dev,655						  PAGE_SIZE,656						  buf->page_list[i].buf,657						  buf->page_list[i].map);658		kfree(buf->page_list);659	}660}661EXPORT_SYMBOL_GPL(mlx4_buf_free);662 663static struct mlx4_db_pgdir *mlx4_alloc_db_pgdir(struct device *dma_device)664{665	struct mlx4_db_pgdir *pgdir;666 667	pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL);668	if (!pgdir)669		return NULL;670 671	bitmap_fill(pgdir->order1, MLX4_DB_PER_PAGE / 2);672	pgdir->bits[0] = pgdir->order0;673	pgdir->bits[1] = pgdir->order1;674	pgdir->db_page = dma_alloc_coherent(dma_device, PAGE_SIZE,675					    &pgdir->db_dma, GFP_KERNEL);676	if (!pgdir->db_page) {677		kfree(pgdir);678		return NULL;679	}680 681	return pgdir;682}683 684static int mlx4_alloc_db_from_pgdir(struct mlx4_db_pgdir *pgdir,685				    struct mlx4_db *db, int order)686{687	int o;688	int i;689 690	for (o = order; o <= 1; ++o) {691		i = find_first_bit(pgdir->bits[o], MLX4_DB_PER_PAGE >> o);692		if (i < MLX4_DB_PER_PAGE >> o)693			goto found;694	}695 696	return -ENOMEM;697 698found:699	clear_bit(i, pgdir->bits[o]);700 701	i <<= o;702 703	if (o > order)704		set_bit(i ^ 1, pgdir->bits[order]);705 706	db->u.pgdir = pgdir;707	db->index   = i;708	db->db      = pgdir->db_page + db->index;709	db->dma     = pgdir->db_dma  + db->index * 4;710	db->order   = order;711 712	return 0;713}714 715int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, int order)716{717	struct mlx4_priv *priv = mlx4_priv(dev);718	struct mlx4_db_pgdir *pgdir;719	int ret = 0;720 721	mutex_lock(&priv->pgdir_mutex);722 723	list_for_each_entry(pgdir, &priv->pgdir_list, list)724		if (!mlx4_alloc_db_from_pgdir(pgdir, db, order))725			goto out;726 727	pgdir = mlx4_alloc_db_pgdir(&dev->persist->pdev->dev);728	if (!pgdir) {729		ret = -ENOMEM;730		goto out;731	}732 733	list_add(&pgdir->list, &priv->pgdir_list);734 735	/* This should never fail -- we just allocated an empty page: */736	WARN_ON(mlx4_alloc_db_from_pgdir(pgdir, db, order));737 738out:739	mutex_unlock(&priv->pgdir_mutex);740 741	return ret;742}743EXPORT_SYMBOL_GPL(mlx4_db_alloc);744 745void mlx4_db_free(struct mlx4_dev *dev, struct mlx4_db *db)746{747	struct mlx4_priv *priv = mlx4_priv(dev);748	int o;749	int i;750 751	mutex_lock(&priv->pgdir_mutex);752 753	o = db->order;754	i = db->index;755 756	if (db->order == 0 && test_bit(i ^ 1, db->u.pgdir->order0)) {757		clear_bit(i ^ 1, db->u.pgdir->order0);758		++o;759	}760	i >>= o;761	set_bit(i, db->u.pgdir->bits[o]);762 763	if (bitmap_full(db->u.pgdir->order1, MLX4_DB_PER_PAGE / 2)) {764		dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE,765				  db->u.pgdir->db_page, db->u.pgdir->db_dma);766		list_del(&db->u.pgdir->list);767		kfree(db->u.pgdir);768	}769 770	mutex_unlock(&priv->pgdir_mutex);771}772EXPORT_SYMBOL_GPL(mlx4_db_free);773 774int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres,775		       int size)776{777	int err;778 779	err = mlx4_db_alloc(dev, &wqres->db, 1);780	if (err)781		return err;782 783	*wqres->db.db = 0;784 785	err = mlx4_buf_direct_alloc(dev, size, &wqres->buf);786	if (err)787		goto err_db;788 789	err = mlx4_mtt_init(dev, wqres->buf.npages, wqres->buf.page_shift,790			    &wqres->mtt);791	if (err)792		goto err_buf;793 794	err = mlx4_buf_write_mtt(dev, &wqres->mtt, &wqres->buf);795	if (err)796		goto err_mtt;797 798	return 0;799 800err_mtt:801	mlx4_mtt_cleanup(dev, &wqres->mtt);802err_buf:803	mlx4_buf_free(dev, size, &wqres->buf);804err_db:805	mlx4_db_free(dev, &wqres->db);806 807	return err;808}809EXPORT_SYMBOL_GPL(mlx4_alloc_hwq_res);810 811void mlx4_free_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres,812		       int size)813{814	mlx4_mtt_cleanup(dev, &wqres->mtt);815	mlx4_buf_free(dev, size, &wqres->buf);816	mlx4_db_free(dev, &wqres->db);817}818EXPORT_SYMBOL_GPL(mlx4_free_hwq_res);819