Blame view

lib/genalloc.c 4.21 KB
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
1
2
3
4
5
6
  /*
   * Basic general purpose allocator for managing special purpose memory
   * not managed by the regular kmalloc/kfree interface.
   * Uses for this includes on-device special memory, uncached memory
   * etc.
   *
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
7
8
9
10
11
12
13
   * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
   *
   * This source code is licensed under the GNU General Public License,
   * Version 2.  See the file COPYING for more details.
   */
  
  #include <linux/module.h>
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
14
  #include <linux/genalloc.h>
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
15

929f97276   Dean Nelson   [PATCH] change ge...
16
17
18
19
20
21
22
  /*
   * Create a new special memory pool.
   *
   * @min_alloc_order: log base 2 of number of bytes each bitmap bit represents
   * @nid: node id of the node the pool structure should be allocated on, or -1
   */
  struct gen_pool *gen_pool_create(int min_alloc_order, int nid)
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
23
  {
929f97276   Dean Nelson   [PATCH] change ge...
24
  	struct gen_pool *pool;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
25

929f97276   Dean Nelson   [PATCH] change ge...
26
27
28
29
30
31
32
  	pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
  	if (pool != NULL) {
  		rwlock_init(&pool->lock);
  		INIT_LIST_HEAD(&pool->chunks);
  		pool->min_alloc_order = min_alloc_order;
  	}
  	return pool;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
33
34
35
36
37
  }
  EXPORT_SYMBOL(gen_pool_create);
  
  
  /*
929f97276   Dean Nelson   [PATCH] change ge...
38
39
40
41
42
43
44
   * Add a new chunk of memory to the specified pool.
   *
   * @pool: pool to add new memory chunk to
   * @addr: starting address of memory chunk to add to pool
   * @size: size in bytes of the memory chunk to add to pool
   * @nid: node id of the node the chunk structure and bitmap should be
   *       allocated on, or -1
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
45
   */
929f97276   Dean Nelson   [PATCH] change ge...
46
47
  int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size,
  		 int nid)
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
48
  {
929f97276   Dean Nelson   [PATCH] change ge...
49
50
51
52
  	struct gen_pool_chunk *chunk;
  	int nbits = size >> pool->min_alloc_order;
  	int nbytes = sizeof(struct gen_pool_chunk) +
  				(nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
53

929f97276   Dean Nelson   [PATCH] change ge...
54
55
56
  	chunk = kmalloc_node(nbytes, GFP_KERNEL, nid);
  	if (unlikely(chunk == NULL))
  		return -1;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
57

929f97276   Dean Nelson   [PATCH] change ge...
58
59
60
61
  	memset(chunk, 0, nbytes);
  	spin_lock_init(&chunk->lock);
  	chunk->start_addr = addr;
  	chunk->end_addr = addr + size;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
62

929f97276   Dean Nelson   [PATCH] change ge...
63
64
65
66
67
  	write_lock(&pool->lock);
  	list_add(&chunk->next_chunk, &pool->chunks);
  	write_unlock(&pool->lock);
  
  	return 0;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
68
  }
929f97276   Dean Nelson   [PATCH] change ge...
69
  EXPORT_SYMBOL(gen_pool_add);
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
70
71
72
  
  
  /*
929f97276   Dean Nelson   [PATCH] change ge...
73
74
75
76
77
   * Allocate the requested number of bytes from the specified pool.
   * Uses a first-fit algorithm.
   *
   * @pool: pool to allocate from
   * @size: number of bytes to allocate from the pool
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
78
   */
929f97276   Dean Nelson   [PATCH] change ge...
79
  unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
80
  {
929f97276   Dean Nelson   [PATCH] change ge...
81
82
83
84
85
  	struct list_head *_chunk;
  	struct gen_pool_chunk *chunk;
  	unsigned long addr, flags;
  	int order = pool->min_alloc_order;
  	int nbits, bit, start_bit, end_bit;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
86

929f97276   Dean Nelson   [PATCH] change ge...
87
88
  	if (size == 0)
  		return 0;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
89

929f97276   Dean Nelson   [PATCH] change ge...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  	nbits = (size + (1UL << order) - 1) >> order;
  
  	read_lock(&pool->lock);
  	list_for_each(_chunk, &pool->chunks) {
  		chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
  
  		end_bit = (chunk->end_addr - chunk->start_addr) >> order;
  		end_bit -= nbits + 1;
  
  		spin_lock_irqsave(&chunk->lock, flags);
  		bit = -1;
  		while (bit + 1 < end_bit) {
  			bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1);
  			if (bit >= end_bit)
  				break;
  
  			start_bit = bit;
  			if (nbits > 1) {
  				bit = find_next_bit(chunk->bits, bit + nbits,
  							bit + 1);
  				if (bit - start_bit < nbits)
  					continue;
  			}
  
  			addr = chunk->start_addr +
  					    ((unsigned long)start_bit << order);
  			while (nbits--)
  				__set_bit(start_bit++, &chunk->bits);
  			spin_unlock_irqrestore(&chunk->lock, flags);
  			read_unlock(&pool->lock);
  			return addr;
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
121
  		}
929f97276   Dean Nelson   [PATCH] change ge...
122
123
124
125
126
127
  		spin_unlock_irqrestore(&chunk->lock, flags);
  	}
  	read_unlock(&pool->lock);
  	return 0;
  }
  EXPORT_SYMBOL(gen_pool_alloc);
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
128

f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
129

929f97276   Dean Nelson   [PATCH] change ge...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  /*
   * Free the specified memory back to the specified pool.
   *
   * @pool: pool to free to
   * @addr: starting address of memory to free back to pool
   * @size: size in bytes of memory to free
   */
  void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
  {
  	struct list_head *_chunk;
  	struct gen_pool_chunk *chunk;
  	unsigned long flags;
  	int order = pool->min_alloc_order;
  	int bit, nbits;
  
  	nbits = (size + (1UL << order) - 1) >> order;
  
  	read_lock(&pool->lock);
  	list_for_each(_chunk, &pool->chunks) {
  		chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
  
  		if (addr >= chunk->start_addr && addr < chunk->end_addr) {
  			BUG_ON(addr + size > chunk->end_addr);
  			spin_lock_irqsave(&chunk->lock, flags);
  			bit = (addr - chunk->start_addr) >> order;
  			while (nbits--)
  				__clear_bit(bit++, &chunk->bits);
  			spin_unlock_irqrestore(&chunk->lock, flags);
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
158
159
  			break;
  		}
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
160
  	}
929f97276   Dean Nelson   [PATCH] change ge...
161
162
  	BUG_ON(nbits > 0);
  	read_unlock(&pool->lock);
f14f75b81   Jes Sorensen   [PATCH] ia64 unca...
163
164
  }
  EXPORT_SYMBOL(gen_pool_free);