Blame view

mm/dmapool.c 13 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>
b95f1b31b   Paul Gortmaker   mm: Map most file...
30
  #include <linux/export.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
  #include <linux/slab.h>
7c77509c5   Paul Gortmaker   mm: fix implicit ...
35
  #include <linux/stat.h>
6182a0943   Matthew Wilcox   dmapool: Tidy up ...
36
37
38
39
  #include <linux/spinlock.h>
  #include <linux/string.h>
  #include <linux/types.h>
  #include <linux/wait.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40

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

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
65
  static DEFINE_MUTEX(pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  
  static ssize_t
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
68
  show_pools(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  {
  	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 ...
83
  	mutex_lock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
  	list_for_each_entry(pool, &dev->dma_pools, pools) {
  		unsigned pages = 0;
  		unsigned blocks = 0;
c49568235   Thomas Gleixner   dmapools: protect...
87
  		spin_lock_irq(&pool->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
  		list_for_each_entry(page, &pool->page_list, page_list) {
  			pages++;
  			blocks += page->in_use;
  		}
c49568235   Thomas Gleixner   dmapools: protect...
92
  		spin_unlock_irq(&pool->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
  
  		/* per-pool info, no real statistics yet */
  		temp = scnprintf(next, size, "%-16s %4u %4Zu %4Zu %2u
  ",
a35a34551   Matthew Wilcox   Change dmapool fr...
97
98
  				 pool->name, blocks,
  				 pages * (pool->allocation / pool->size),
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
99
  				 pool->size, pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
  		size -= temp;
  		next += temp;
  	}
b2366d68d   Matthias Kaehlcke   Driver core: use ...
103
  	mutex_unlock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
  
  	return PAGE_SIZE - size;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
107
108
  
  static DEVICE_ATTR(pools, S_IRUGO, show_pools, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
  
  /**
   * 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...
116
   * @boundary: returned blocks won't cross this power of two boundary
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
   * 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...
126
   * If @boundary is nonzero, objects returned from dma_pool_alloc() won't
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
   * 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...
131
  struct dma_pool *dma_pool_create(const char *name, struct device *dev,
e34f44b35   Matthew Wilcox   pool: Improve mem...
132
  				 size_t size, size_t align, size_t boundary)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
134
  	struct dma_pool *retval;
e34f44b35   Matthew Wilcox   pool: Improve mem...
135
  	size_t allocation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

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

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

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

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

b5ee5befa   Andi Kleen   dmapool: enable d...
239
  #ifdef	DMAPOOL_DEBUG
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
240
  	memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
242
243
244
  	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
245
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
253
  /**
   * 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...
254
  void dma_pool_destroy(struct dma_pool *pool)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  {
b2366d68d   Matthias Kaehlcke   Driver core: use ...
256
  	mutex_lock(&pools_lock);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
257
258
259
  	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 ...
260
  	mutex_unlock(&pools_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
262
263
264
265
  	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...
266
  		if (is_page_busy(page)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  			if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
268
269
270
  				dev_err(pool->dev,
  					"dma_pool_destroy %s, %p busy
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
  					pool->name, page->vaddr);
  			else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
273
274
275
276
  				printk(KERN_ERR
  				       "dma_pool_destroy %s, %p busy
  ",
  				       pool->name, page->vaddr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  			/* leak the still-in-use consistent memory */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
278
279
  			list_del(&page->page_list);
  			kfree(page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  		} else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
281
  			pool_free_page(pool, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
283
  	kfree(pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
285
  EXPORT_SYMBOL(dma_pool_destroy);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
292
293
294
  
  /**
   * 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 ...
295
   * If such a memory block can't be allocated, %NULL is returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
296
   */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
297
298
  void *dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
  		     dma_addr_t *handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
300
301
  	unsigned long flags;
  	struct dma_page *page;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
302
303
  	size_t offset;
  	void *retval;
ea05c8444   Dima Zavin   mm: add a might_s...
304
  	might_sleep_if(mem_flags & __GFP_WAIT);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
305
  	spin_lock_irqsave(&pool->lock, flags);
2cae367e4   Matthew Wilcox   Avoid taking wait...
306
   restart:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	list_for_each_entry(page, &pool->page_list, page_list) {
a35a34551   Matthew Wilcox   Change dmapool fr...
308
309
  		if (page->offset < pool->allocation)
  			goto ready;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
311
312
  	page = pool_alloc_page(pool, GFP_ATOMIC);
  	if (!page) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  		if (mem_flags & __GFP_WAIT) {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
314
  			DECLARE_WAITQUEUE(wait, current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315

684265d4a   Andrew Morton   mm/dmapool.c: use...
316
  			__set_current_state(TASK_UNINTERRUPTIBLE);
2cae367e4   Matthew Wilcox   Avoid taking wait...
317
  			__add_wait_queue(&pool->waitq, &wait);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
318
  			spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319

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

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

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
  	list_for_each_entry(page, &pool->page_list, page_list) {
  		if (dma < page->dma)
  			continue;
  		if (dma < (page->dma + pool->allocation))
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
352
  			return page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	}
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
354
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
360
361
362
363
364
  /**
   * 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...
365
  void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
367
368
  	struct dma_page *page;
  	unsigned long flags;
a35a34551   Matthew Wilcox   Change dmapool fr...
369
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370

84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
371
  	spin_lock_irqsave(&pool->lock, flags);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
372
373
  	page = pool_find_page(pool, dma);
  	if (!page) {
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
374
  		spin_unlock_irqrestore(&pool->lock, flags);
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) {
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
389
  		spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
  		if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
391
392
393
394
  			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
395
  		else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
396
397
398
399
  			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
400
401
  		return;
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
402
403
404
405
406
407
408
  	{
  		unsigned int chain = page->offset;
  		while (chain < pool->allocation) {
  			if (chain != offset) {
  				chain = *(int *)(page->vaddr + chain);
  				continue;
  			}
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
409
  			spin_unlock_irqrestore(&pool->lock, flags);
a35a34551   Matthew Wilcox   Change dmapool fr...
410
411
412
413
414
415
416
417
418
419
420
421
  			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
422
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
423
  	memset(vaddr, POOL_POISON_FREED, pool->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  	page->in_use--;
a35a34551   Matthew Wilcox   Change dmapool fr...
426
427
  	*(int *)vaddr = page->offset;
  	page->offset = offset;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
428
  	if (waitqueue_active(&pool->waitq))
2cae367e4   Matthew Wilcox   Avoid taking wait...
429
  		wake_up_locked(&pool->waitq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
  	/*
  	 * Resist a temptation to do
a35a34551   Matthew Wilcox   Change dmapool fr...
432
  	 *    if (!is_page_busy(page)) pool_free_page(pool, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
  	 * Better have a few empty pages hang around.
  	 */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
435
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
437
  EXPORT_SYMBOL(dma_pool_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
438

9ac7849e3   Tejun Heo   devres: device re...
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
481
  /*
   * 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...
482
  EXPORT_SYMBOL(dmam_pool_create);
9ac7849e3   Tejun Heo   devres: device re...
483
484
485
486
487
488
489
490
491
492
  
  /**
   * 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;
9ac7849e3   Tejun Heo   devres: device re...
493
  	WARN_ON(devres_destroy(dev, dmam_pool_release, dmam_pool_match, pool));
ae891a1b9   Maxin B John   devres: fix possi...
494
  	dma_pool_destroy(pool);
9ac7849e3   Tejun Heo   devres: device re...
495
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
496
  EXPORT_SYMBOL(dmam_pool_destroy);