Blame view

mm/dmapool.c 12.9 KB
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * DMA Pool allocator
   *
   * Copyright 2001 David Brownell
   * Copyright 2007 Intel Corporation
   *   Author: Matthew Wilcox <willy@linux.intel.com>
   *
   * This software may be redistributed and/or modified under the terms of
   * the GNU General Public License ("GPL") version 2 as published by the
   * Free Software Foundation.
   *
   * This allocator returns small blocks of a given size which are DMA-able by
   * the given device.  It uses the dma_alloc_coherent page allocator to get
   * new pages, then splits them up into blocks of the required size.
   * Many older drivers still have their own code to do this.
   *
   * The current design of this allocator is fairly simple.  The pool is
   * represented by the 'struct dma_pool' which keeps a doubly-linked list of
   * allocated pages.  Each page in the page_list is split into blocks of at
a35a34551   Matthew Wilcox   Change dmapool fr...
20
21
22
   * least 'size' bytes.  Free blocks are tracked in an unsorted singly-linked
   * list of free blocks within the page.  Used blocks aren't tracked, but we
   * keep a count of how many are currently allocated from each page.
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
23
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
  
  #include <linux/device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
  #include <linux/dma-mapping.h>
  #include <linux/dmapool.h>
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
28
29
  #include <linux/kernel.h>
  #include <linux/list.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include <linux/module.h>
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
31
  #include <linux/mutex.h>
c9cf55285   Randy Dunlap   [PATCH] add poiso...
32
  #include <linux/poison.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
33
  #include <linux/sched.h>
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
34
35
36
37
38
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/string.h>
  #include <linux/types.h>
  #include <linux/wait.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39

b5ee5befa   Andi Kleen   dmapool: enable d...
40
41
42
  #if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_SLUB_DEBUG_ON)
  #define DMAPOOL_DEBUG 1
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
43
44
45
  struct dma_pool {		/* the pool */
  	struct list_head page_list;
  	spinlock_t lock;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
46
47
48
  	size_t size;
  	struct device *dev;
  	size_t allocation;
e34f44b35   Matthew Wilcox   pool: Improve mem...
49
  	size_t boundary;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
50
51
52
  	char name[32];
  	wait_queue_head_t waitq;
  	struct list_head pools;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  };
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
54
55
56
57
  struct dma_page {		/* cacheable header for 'allocation' bytes */
  	struct list_head page_list;
  	void *vaddr;
  	dma_addr_t dma;
a35a34551   Matthew Wilcox   Change dmapool fr...
58
59
  	unsigned int in_use;
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
  };
  
  #define	POOL_TIMEOUT_JIFFIES	((100 /* msec */ * HZ) / 1000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
64
  static DEFINE_MUTEX(pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
  
  static ssize_t
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
67
  show_pools(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  {
  	unsigned temp;
  	unsigned size;
  	char *next;
  	struct dma_page *page;
  	struct dma_pool *pool;
  
  	next = buf;
  	size = PAGE_SIZE;
  
  	temp = scnprintf(next, size, "poolinfo - 0.1
  ");
  	size -= temp;
  	next += temp;
b2366d68d   Matthias Kaehlcke   Driver core: use ...
82
  	mutex_lock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
85
86
87
88
89
90
91
92
93
94
  	list_for_each_entry(pool, &dev->dma_pools, pools) {
  		unsigned pages = 0;
  		unsigned blocks = 0;
  
  		list_for_each_entry(page, &pool->page_list, page_list) {
  			pages++;
  			blocks += page->in_use;
  		}
  
  		/* per-pool info, no real statistics yet */
  		temp = scnprintf(next, size, "%-16s %4u %4Zu %4Zu %2u
  ",
a35a34551   Matthew Wilcox   Change dmapool fr...
95
96
  				 pool->name, blocks,
  				 pages * (pool->allocation / pool->size),
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
97
  				 pool->size, pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  		size -= temp;
  		next += temp;
  	}
b2366d68d   Matthias Kaehlcke   Driver core: use ...
101
  	mutex_unlock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
  
  	return PAGE_SIZE - size;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
105
106
  
  static DEVICE_ATTR(pools, S_IRUGO, show_pools, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
  
  /**
   * dma_pool_create - Creates a pool of consistent memory blocks, for dma.
   * @name: name of pool, for diagnostics
   * @dev: device that will be doing the DMA
   * @size: size of the blocks in this pool.
   * @align: alignment requirement for blocks; must be a power of two
e34f44b35   Matthew Wilcox   pool: Improve mem...
114
   * @boundary: returned blocks won't cross this power of two boundary
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
   * Context: !in_interrupt()
   *
   * Returns a dma allocation pool with the requested characteristics, or
   * null if one can't be created.  Given one of these pools, dma_pool_alloc()
   * may be used to allocate memory.  Such memory will all have "consistent"
   * DMA mappings, accessible by the device and its driver without using
   * cache flushing primitives.  The actual size of blocks allocated may be
   * larger than requested because of alignment.
   *
e34f44b35   Matthew Wilcox   pool: Improve mem...
124
   * If @boundary is nonzero, objects returned from dma_pool_alloc() won't
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
   * cross that size boundary.  This is useful for devices which have
   * addressing restrictions on individual DMA transfers, such as not crossing
   * boundaries of 4KBytes.
   */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
129
  struct dma_pool *dma_pool_create(const char *name, struct device *dev,
e34f44b35   Matthew Wilcox   pool: Improve mem...
130
  				 size_t size, size_t align, size_t boundary)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
132
  	struct dma_pool *retval;
e34f44b35   Matthew Wilcox   pool: Improve mem...
133
  	size_t allocation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

399154be2   Matthew Wilcox   dmapool: Validate...
135
  	if (align == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  		align = 1;
399154be2   Matthew Wilcox   dmapool: Validate...
137
  	} else if (align & (align - 1)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
140
  	if (size == 0) {
399154be2   Matthew Wilcox   dmapool: Validate...
141
  		return NULL;
a35a34551   Matthew Wilcox   Change dmapool fr...
142
143
144
  	} else if (size < 4) {
  		size = 4;
  	}
399154be2   Matthew Wilcox   dmapool: Validate...
145
146
147
  
  	if ((size % align) != 0)
  		size = ALIGN(size, align);
e34f44b35   Matthew Wilcox   pool: Improve mem...
148
149
150
151
152
  	allocation = max_t(size_t, size, PAGE_SIZE);
  
  	if (!boundary) {
  		boundary = allocation;
  	} else if ((boundary < size) || (boundary & (boundary - 1))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
  		return NULL;
e34f44b35   Matthew Wilcox   pool: Improve mem...
154
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155

e34f44b35   Matthew Wilcox   pool: Improve mem...
156
157
  	retval = kmalloc_node(sizeof(*retval), GFP_KERNEL, dev_to_node(dev));
  	if (!retval)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
  		return retval;
e34f44b35   Matthew Wilcox   pool: Improve mem...
159
  	strlcpy(retval->name, name, sizeof(retval->name));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  
  	retval->dev = dev;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
162
163
  	INIT_LIST_HEAD(&retval->page_list);
  	spin_lock_init(&retval->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  	retval->size = size;
e34f44b35   Matthew Wilcox   pool: Improve mem...
165
  	retval->boundary = boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  	retval->allocation = allocation;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
167
  	init_waitqueue_head(&retval->waitq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
  
  	if (dev) {
141ecc532   Cornelia Huck   driver core fixes...
170
  		int ret;
b2366d68d   Matthias Kaehlcke   Driver core: use ...
171
  		mutex_lock(&pools_lock);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
172
173
  		if (list_empty(&dev->dma_pools))
  			ret = device_create_file(dev, &dev_attr_pools);
141ecc532   Cornelia Huck   driver core fixes...
174
175
  		else
  			ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  		/* note:  not currently insisting "name" be unique */
141ecc532   Cornelia Huck   driver core fixes...
177
  		if (!ret)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
178
  			list_add(&retval->pools, &dev->dma_pools);
141ecc532   Cornelia Huck   driver core fixes...
179
180
181
182
  		else {
  			kfree(retval);
  			retval = NULL;
  		}
b2366d68d   Matthias Kaehlcke   Driver core: use ...
183
  		mutex_unlock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  	} else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
185
  		INIT_LIST_HEAD(&retval->pools);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
  
  	return retval;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
189
  EXPORT_SYMBOL(dma_pool_create);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190

a35a34551   Matthew Wilcox   Change dmapool fr...
191
192
193
  static void pool_initialise_page(struct dma_pool *pool, struct dma_page *page)
  {
  	unsigned int offset = 0;
e34f44b35   Matthew Wilcox   pool: Improve mem...
194
  	unsigned int next_boundary = pool->boundary;
a35a34551   Matthew Wilcox   Change dmapool fr...
195
196
197
  
  	do {
  		unsigned int next = offset + pool->size;
e34f44b35   Matthew Wilcox   pool: Improve mem...
198
199
200
201
  		if (unlikely((next + pool->size) >= next_boundary)) {
  			next = next_boundary;
  			next_boundary += pool->boundary;
  		}
a35a34551   Matthew Wilcox   Change dmapool fr...
202
203
204
205
  		*(int *)(page->vaddr + offset) = next;
  		offset = next;
  	} while (offset < pool->allocation);
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
206
  static struct dma_page *pool_alloc_page(struct dma_pool *pool, gfp_t mem_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
208
  	struct dma_page *page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209

a35a34551   Matthew Wilcox   Change dmapool fr...
210
  	page = kmalloc(sizeof(*page), mem_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  	if (!page)
  		return NULL;
a35a34551   Matthew Wilcox   Change dmapool fr...
213
  	page->vaddr = dma_alloc_coherent(pool->dev, pool->allocation,
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
214
  					 &page->dma, mem_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  	if (page->vaddr) {
b5ee5befa   Andi Kleen   dmapool: enable d...
216
  #ifdef	DMAPOOL_DEBUG
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
217
  		memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
  #endif
a35a34551   Matthew Wilcox   Change dmapool fr...
219
  		pool_initialise_page(pool, page);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
220
  		list_add(&page->page_list, &pool->page_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  		page->in_use = 0;
a35a34551   Matthew Wilcox   Change dmapool fr...
222
  		page->offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
  	} else {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
224
  		kfree(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
  		page = NULL;
  	}
  	return page;
  }
a35a34551   Matthew Wilcox   Change dmapool fr...
229
  static inline int is_page_busy(struct dma_page *page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  {
a35a34551   Matthew Wilcox   Change dmapool fr...
231
  	return page->in_use != 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
233
  static void pool_free_page(struct dma_pool *pool, struct dma_page *page)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
235
  	dma_addr_t dma = page->dma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236

b5ee5befa   Andi Kleen   dmapool: enable d...
237
  #ifdef	DMAPOOL_DEBUG
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
238
  	memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
240
241
242
  	dma_free_coherent(pool->dev, pool->allocation, page->vaddr, dma);
  	list_del(&page->page_list);
  	kfree(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
251
  /**
   * dma_pool_destroy - destroys a pool of dma memory blocks.
   * @pool: dma pool that will be destroyed
   * Context: !in_interrupt()
   *
   * Caller guarantees that no more memory from the pool is in use,
   * and that nothing will try to use the pool after this call.
   */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
252
  void dma_pool_destroy(struct dma_pool *pool)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  {
b2366d68d   Matthias Kaehlcke   Driver core: use ...
254
  	mutex_lock(&pools_lock);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
255
256
257
  	list_del(&pool->pools);
  	if (pool->dev && list_empty(&pool->dev->dma_pools))
  		device_remove_file(pool->dev, &dev_attr_pools);
b2366d68d   Matthias Kaehlcke   Driver core: use ...
258
  	mutex_unlock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
260
261
262
263
  	while (!list_empty(&pool->page_list)) {
  		struct dma_page *page;
  		page = list_entry(pool->page_list.next,
  				  struct dma_page, page_list);
a35a34551   Matthew Wilcox   Change dmapool fr...
264
  		if (is_page_busy(page)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  			if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
266
267
268
  				dev_err(pool->dev,
  					"dma_pool_destroy %s, %p busy
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
  					pool->name, page->vaddr);
  			else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
271
272
273
274
  				printk(KERN_ERR
  				       "dma_pool_destroy %s, %p busy
  ",
  				       pool->name, page->vaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  			/* leak the still-in-use consistent memory */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
276
277
  			list_del(&page->page_list);
  			kfree(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  		} else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
279
  			pool_free_page(pool, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
281
  	kfree(pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
283
  EXPORT_SYMBOL(dma_pool_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
289
290
291
292
  
  /**
   * dma_pool_alloc - get a block of consistent memory
   * @pool: dma pool that will produce the block
   * @mem_flags: GFP_* bitmask
   * @handle: pointer to dma address of block
   *
   * This returns the kernel virtual address of a currently unused block,
   * and reports its dma address through the handle.
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
293
   * If such a memory block can't be allocated, %NULL is returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
   */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
295
296
  void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
  		     dma_addr_t *handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
298
299
  	unsigned long flags;
  	struct dma_page *page;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
300
301
  	size_t offset;
  	void *retval;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
302
  	spin_lock_irqsave(&pool->lock, flags);
2cae367e4   Matthew Wilcox   Avoid taking wait...
303
   restart:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	list_for_each_entry(page, &pool->page_list, page_list) {
a35a34551   Matthew Wilcox   Change dmapool fr...
305
306
  		if (page->offset < pool->allocation)
  			goto ready;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
308
309
  	page = pool_alloc_page(pool, GFP_ATOMIC);
  	if (!page) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  		if (mem_flags & __GFP_WAIT) {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
311
  			DECLARE_WAITQUEUE(wait, current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312

d9aacccf4   Arjan van de Ven   make dmapool code...
313
  			__set_current_state(TASK_INTERRUPTIBLE);
2cae367e4   Matthew Wilcox   Avoid taking wait...
314
  			__add_wait_queue(&pool->waitq, &wait);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
315
  			spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
317
  			schedule_timeout(POOL_TIMEOUT_JIFFIES);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318

2cae367e4   Matthew Wilcox   Avoid taking wait...
319
320
  			spin_lock_irqsave(&pool->lock, flags);
  			__remove_wait_queue(&pool->waitq, &wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
322
323
324
325
  			goto restart;
  		}
  		retval = NULL;
  		goto done;
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
326
   ready:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  	page->in_use++;
a35a34551   Matthew Wilcox   Change dmapool fr...
328
329
  	offset = page->offset;
  	page->offset = *(int *)(page->vaddr + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  	retval = offset + page->vaddr;
  	*handle = offset + page->dma;
b5ee5befa   Andi Kleen   dmapool: enable d...
332
  #ifdef	DMAPOOL_DEBUG
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
333
  	memset(retval, POOL_POISON_ALLOCATED, pool->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
335
336
   done:
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  	return retval;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
339
  EXPORT_SYMBOL(dma_pool_alloc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
341
  static struct dma_page *pool_find_page(struct dma_pool *pool, dma_addr_t dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
343
344
  	unsigned long flags;
  	struct dma_page *page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
346
  	spin_lock_irqsave(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
  	list_for_each_entry(page, &pool->page_list, page_list) {
  		if (dma < page->dma)
  			continue;
  		if (dma < (page->dma + pool->allocation))
  			goto done;
  	}
  	page = NULL;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
354
355
   done:
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
  	return page;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
366
  /**
   * dma_pool_free - put block back into dma pool
   * @pool: the dma pool holding the block
   * @vaddr: virtual address of block
   * @dma: dma address of block
   *
   * Caller promises neither device nor driver will again touch this block
   * unless it is first re-allocated.
   */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
367
  void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
369
370
  	struct dma_page *page;
  	unsigned long flags;
a35a34551   Matthew Wilcox   Change dmapool fr...
371
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
373
374
  	page = pool_find_page(pool, dma);
  	if (!page) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  		if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
376
377
378
379
  			dev_err(pool->dev,
  				"dma_pool_free %s, %p/%lx (bad dma)
  ",
  				pool->name, vaddr, (unsigned long)dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  		else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
381
382
383
  			printk(KERN_ERR "dma_pool_free %s, %p/%lx (bad dma)
  ",
  			       pool->name, vaddr, (unsigned long)dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
  		return;
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
386
  	offset = vaddr - page->vaddr;
b5ee5befa   Andi Kleen   dmapool: enable d...
387
  #ifdef	DMAPOOL_DEBUG
a35a34551   Matthew Wilcox   Change dmapool fr...
388
  	if ((dma - page->dma) != offset) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
  		if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
390
391
392
393
  			dev_err(pool->dev,
  				"dma_pool_free %s, %p (bad vaddr)/%Lx
  ",
  				pool->name, vaddr, (unsigned long long)dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
  		else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
395
396
397
398
  			printk(KERN_ERR
  			       "dma_pool_free %s, %p (bad vaddr)/%Lx
  ",
  			       pool->name, vaddr, (unsigned long long)dma);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
  		return;
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	{
  		unsigned int chain = page->offset;
  		while (chain < pool->allocation) {
  			if (chain != offset) {
  				chain = *(int *)(page->vaddr + chain);
  				continue;
  			}
  			if (pool->dev)
  				dev_err(pool->dev, "dma_pool_free %s, dma %Lx "
  					"already free
  ", pool->name,
  					(unsigned long long)dma);
  			else
  				printk(KERN_ERR "dma_pool_free %s, dma %Lx "
  					"already free
  ", pool->name,
  					(unsigned long long)dma);
  			return;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
421
  	memset(vaddr, POOL_POISON_FREED, pool->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
423
  	spin_lock_irqsave(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
  	page->in_use--;
a35a34551   Matthew Wilcox   Change dmapool fr...
425
426
  	*(int *)vaddr = page->offset;
  	page->offset = offset;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
427
  	if (waitqueue_active(&pool->waitq))
2cae367e4   Matthew Wilcox   Avoid taking wait...
428
  		wake_up_locked(&pool->waitq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
  	/*
  	 * Resist a temptation to do
a35a34551   Matthew Wilcox   Change dmapool fr...
431
  	 *    if (!is_page_busy(page)) pool_free_page(pool, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
  	 * Better have a few empty pages hang around.
  	 */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
434
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
436
  EXPORT_SYMBOL(dma_pool_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437

9ac7849e3   Tejun Heo   devres: device re...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  /*
   * Managed DMA pool
   */
  static void dmam_pool_release(struct device *dev, void *res)
  {
  	struct dma_pool *pool = *(struct dma_pool **)res;
  
  	dma_pool_destroy(pool);
  }
  
  static int dmam_pool_match(struct device *dev, void *res, void *match_data)
  {
  	return *(struct dma_pool **)res == match_data;
  }
  
  /**
   * dmam_pool_create - Managed dma_pool_create()
   * @name: name of pool, for diagnostics
   * @dev: device that will be doing the DMA
   * @size: size of the blocks in this pool.
   * @align: alignment requirement for blocks; must be a power of two
   * @allocation: returned blocks won't cross this boundary (or zero)
   *
   * Managed dma_pool_create().  DMA pool created with this function is
   * automatically destroyed on driver detach.
   */
  struct dma_pool *dmam_pool_create(const char *name, struct device *dev,
  				  size_t size, size_t align, size_t allocation)
  {
  	struct dma_pool **ptr, *pool;
  
  	ptr = devres_alloc(dmam_pool_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return NULL;
  
  	pool = *ptr = dma_pool_create(name, dev, size, align, allocation);
  	if (pool)
  		devres_add(dev, ptr);
  	else
  		devres_free(ptr);
  
  	return pool;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
481
  EXPORT_SYMBOL(dmam_pool_create);
9ac7849e3   Tejun Heo   devres: device re...
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  
  /**
   * dmam_pool_destroy - Managed dma_pool_destroy()
   * @pool: dma pool that will be destroyed
   *
   * Managed dma_pool_destroy().
   */
  void dmam_pool_destroy(struct dma_pool *pool)
  {
  	struct device *dev = pool->dev;
  
  	dma_pool_destroy(pool);
  	WARN_ON(devres_destroy(dev, dmam_pool_release, dmam_pool_match, pool));
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
496
  EXPORT_SYMBOL(dmam_pool_destroy);