2 /****************************************************************************
3 * Driver for Solarflare network controllers -
4 * resource management for Xen backend, OpenOnload, etc
5 * (including support for SFE4001 10GBT NIC)
7 * This file contains implementation of a buddy allocator.
9 * Copyright 2005-2007: Solarflare Communications Inc,
10 * 9501 Jeronimo Road, Suite 250,
11 * Irvine, CA 92618, USA
13 * Developed and maintained by Solarflare Communications:
14 * <linux-xen-drivers@solarflare.com>
15 * <onload-dev@solarflare.com>
17 * Certain parts of the driver were implemented by
18 * Alexandra Kossovsky <Alexandra.Kossovsky@oktetlabs.ru>
19 * OKTET Labs Ltd, Russia,
20 * http://oktetlabs.ru, <info@oktetlabs.ru>
21 * by request of Solarflare Communications
24 * This program is free software; you can redistribute it and/or modify it
25 * under the terms of the GNU General Public License version 2 as published
26 * by the Free Software Foundation, incorporated herein by reference.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36 ****************************************************************************
39 #include <ci/efhw/common.h> /* get uintXX types on win32 */
40 #include <ci/efrm/sysdep.h>
41 #include <ci/efrm/buddy.h>
42 #include <ci/efrm/debug.h>
45 #define DEBUG_ALLOC(x)
47 #define DEBUG_ALLOC(x) x
49 static inline void efrm_buddy_dump(struct efrm_buddy_allocator *b)
53 EFRM_NOTICE("%s: dump allocator with order %u",
55 for (o = 0; o <= b->order; o++) {
56 struct list_head *l = &b->free_lists[o];
57 while (l->next != &b->free_lists[o]) {
59 EFRM_NOTICE("%s: order %x: %zx", __func__, o,
67 * The purpose of the following inline functions is to give the
68 * understandable names to the simple actions.
71 efrm_buddy_free_list_add(struct efrm_buddy_allocator *b,
72 unsigned order, unsigned addr)
74 list_add(&b->links[addr], &b->free_lists[order]);
75 b->orders[addr] = (uint8_t) order;
78 efrm_buddy_free_list_del(struct efrm_buddy_allocator *b, unsigned addr)
80 list_del(&b->links[addr]);
81 b->links[addr].next = NULL;
84 efrm_buddy_free_list_empty(struct efrm_buddy_allocator *b, unsigned order)
86 return list_empty(&b->free_lists[order]);
88 static inline unsigned
89 efrm_buddy_free_list_pop(struct efrm_buddy_allocator *b, unsigned order)
91 struct list_head *l = list_pop(&b->free_lists[order]);
93 return (unsigned)(l - b->links);
96 efrm_buddy_addr_in_free_list(struct efrm_buddy_allocator *b, unsigned addr)
98 return b->links[addr].next != NULL;
100 static inline unsigned
101 efrm_buddy_free_list_first(struct efrm_buddy_allocator *b, unsigned order)
103 return (unsigned)(b->free_lists[order].next - b->links);
106 int efrm_buddy_ctor(struct efrm_buddy_allocator *b, unsigned order)
109 unsigned size = 1 << order;
111 DEBUG_ALLOC(EFRM_NOTICE("%s(%u)", __func__, order));
113 EFRM_ASSERT(order <= sizeof(unsigned) * 8 - 1);
116 b->free_lists = vmalloc((order + 1) * sizeof(struct list_head));
117 if (b->free_lists == NULL)
120 b->links = vmalloc(size * sizeof(struct list_head));
121 if (b->links == NULL)
124 b->orders = vmalloc(size);
125 if (b->orders == NULL)
128 memset(b->links, 0, size * sizeof(struct list_head));
130 for (o = 0; o <= b->order; ++o)
131 INIT_LIST_HEAD(b->free_lists + o);
133 efrm_buddy_free_list_add(b, b->order, 0);
140 vfree(b->free_lists);
145 void efrm_buddy_dtor(struct efrm_buddy_allocator *b)
149 vfree(b->free_lists);
154 int efrm_buddy_alloc(struct efrm_buddy_allocator *b, unsigned order)
159 DEBUG_ALLOC(EFRM_NOTICE("%s(%u)", __func__, order));
162 /* Find smallest chunk that is big enough. ?? Can optimise this by
163 ** keeping array of pointers to smallest chunk for each order.
166 while (smallest <= b->order &&
167 efrm_buddy_free_list_empty(b, smallest))
170 if (smallest > b->order) {
171 DEBUG_ALLOC(EFRM_NOTICE
172 ("buddy - alloc order %d failed - max order %d",
177 /* Split blocks until we get one of the correct size. */
178 addr = efrm_buddy_free_list_pop(b, smallest);
180 DEBUG_ALLOC(EFRM_NOTICE("buddy - alloc %x order %d cut from order %d",
181 addr, order, smallest););
182 while (smallest-- > order)
183 efrm_buddy_free_list_add(b, smallest, addr + (1 << smallest));
185 EFRM_DO_DEBUG(b->orders[addr] = (uint8_t) order);
187 EFRM_ASSERT(addr < 1u << b->order);
192 efrm_buddy_free(struct efrm_buddy_allocator *b, unsigned addr,
197 DEBUG_ALLOC(EFRM_NOTICE("%s(%u, %u)", __func__, addr, order));
199 EFRM_ASSERT(order <= b->order);
200 EFRM_ASSERT((unsigned long)addr + ((unsigned long)1 << order) <=
201 (unsigned long)1 << b->order);
202 EFRM_ASSERT(!efrm_buddy_addr_in_free_list(b, addr));
203 EFRM_ASSERT(b->orders[addr] == order);
205 /* merge free blocks */
206 while (order < b->order) {
207 buddy_addr = addr ^ (1 << order);
208 if (!efrm_buddy_addr_in_free_list(b, buddy_addr) ||
209 b->orders[buddy_addr] != order)
211 efrm_buddy_free_list_del(b, buddy_addr);
212 if (buddy_addr < addr)
217 DEBUG_ALLOC(EFRM_NOTICE
218 ("buddy - free %x merged into order %d", addr, order););
219 efrm_buddy_free_list_add(b, order, addr);