Blame view

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

baa2ef839   Paul McQuade   mm/dmapool.c: fix...
135
  	if (align == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  		align = 1;
baa2ef839   Paul McQuade   mm/dmapool.c: fix...
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

baa2ef839   Paul McQuade   mm/dmapool.c: fix...
140
  	if (size == 0)
399154be2   Matthew Wilcox   dmapool: Validate...
141
  		return NULL;
baa2ef839   Paul McQuade   mm/dmapool.c: fix...
142
  	else if (size < 4)
a35a34551   Matthew Wilcox   Change dmapool fr...
143
  		size = 4;
399154be2   Matthew Wilcox   dmapool: Validate...
144
145
146
  
  	if ((size % align) != 0)
  		size = ALIGN(size, align);
e34f44b35   Matthew Wilcox   pool: Improve mem...
147
  	allocation = max_t(size_t, size, PAGE_SIZE);
baa2ef839   Paul McQuade   mm/dmapool.c: fix...
148
  	if (!boundary)
e34f44b35   Matthew Wilcox   pool: Improve mem...
149
  		boundary = allocation;
baa2ef839   Paul McQuade   mm/dmapool.c: fix...
150
  	else if ((boundary < size) || (boundary & (boundary - 1)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  		return NULL;
e34f44b35   Matthew Wilcox   pool: Improve mem...
152
153
  	retval = kmalloc_node(sizeof(*retval), GFP_KERNEL, dev_to_node(dev));
  	if (!retval)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  		return retval;
e34f44b35   Matthew Wilcox   pool: Improve mem...
155
  	strlcpy(retval->name, name, sizeof(retval->name));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
  
  	retval->dev = dev;
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
158
159
  	INIT_LIST_HEAD(&retval->page_list);
  	spin_lock_init(&retval->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
  	retval->size = size;
e34f44b35   Matthew Wilcox   pool: Improve mem...
161
  	retval->boundary = boundary;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  	retval->allocation = allocation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163

cc6b664aa   Daeseok Youn   mm/dmapool.c: rem...
164
  	INIT_LIST_HEAD(&retval->pools);
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
165
166
167
168
169
170
171
172
173
  	/*
  	 * pools_lock ensures that the ->dma_pools list does not get corrupted.
  	 * pools_reg_lock ensures that there is not a race between
  	 * dma_pool_create() and dma_pool_destroy() or within dma_pool_create()
  	 * when the first invocation of dma_pool_create() failed on
  	 * device_create_file() and the second assumes that it has been done (I
  	 * know it is a short window).
  	 */
  	mutex_lock(&pools_reg_lock);
cc6b664aa   Daeseok Youn   mm/dmapool.c: rem...
174
  	mutex_lock(&pools_lock);
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
175
176
177
  	if (list_empty(&dev->dma_pools))
  		empty = true;
  	list_add(&retval->pools, &dev->dma_pools);
cc6b664aa   Daeseok Youn   mm/dmapool.c: rem...
178
  	mutex_unlock(&pools_lock);
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	if (empty) {
  		int err;
  
  		err = device_create_file(dev, &dev_attr_pools);
  		if (err) {
  			mutex_lock(&pools_lock);
  			list_del(&retval->pools);
  			mutex_unlock(&pools_lock);
  			mutex_unlock(&pools_reg_lock);
  			kfree(retval);
  			return NULL;
  		}
  	}
  	mutex_unlock(&pools_reg_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
  	return retval;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
195
  EXPORT_SYMBOL(dma_pool_create);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196

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

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

b5ee5befa   Andi Kleen   dmapool: enable d...
242
  #ifdef	DMAPOOL_DEBUG
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
243
  	memset(page->vaddr, POOL_POISON_FREED, pool->allocation);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
245
246
247
  	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
248
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
250
251
252
253
254
255
256
  /**
   * 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...
257
  void dma_pool_destroy(struct dma_pool *pool)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  {
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
259
260
261
  	bool empty = false;
  
  	mutex_lock(&pools_reg_lock);
b2366d68d   Matthias Kaehlcke   Driver core: use ...
262
  	mutex_lock(&pools_lock);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
263
264
  	list_del(&pool->pools);
  	if (pool->dev && list_empty(&pool->dev->dma_pools))
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
265
  		empty = true;
b2366d68d   Matthias Kaehlcke   Driver core: use ...
266
  	mutex_unlock(&pools_lock);
01c2965f0   Sebastian Andrzej Siewior   mm: dmapool: add/...
267
268
269
  	if (empty)
  		device_remove_file(pool->dev, &dev_attr_pools);
  	mutex_unlock(&pools_reg_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270

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

387870f2d   Marek Szyprowski   mm: dmapool: use ...
320
321
  	/* pool_alloc_page() might sleep, so temporarily drop &pool->lock */
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322

387870f2d   Marek Szyprowski   mm: dmapool: use ...
323
324
325
  	page = pool_alloc_page(pool, mem_flags);
  	if (!page)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326

387870f2d   Marek Szyprowski   mm: dmapool: use ...
327
  	spin_lock_irqsave(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328

387870f2d   Marek Szyprowski   mm: dmapool: use ...
329
  	list_add(&page->page_list, &pool->page_list);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
330
   ready:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  	page->in_use++;
a35a34551   Matthew Wilcox   Change dmapool fr...
332
333
  	offset = page->offset;
  	page->offset = *(int *)(page->vaddr + offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  	retval = offset + page->vaddr;
  	*handle = offset + page->dma;
b5ee5befa   Andi Kleen   dmapool: enable d...
336
  #ifdef	DMAPOOL_DEBUG
5de55b265   Matthieu CASTET   dmapool: make DMA...
337
338
339
340
341
342
343
344
345
  	{
  		int i;
  		u8 *data = retval;
  		/* page->offset is stored in first 4 bytes */
  		for (i = sizeof(page->offset); i < pool->size; i++) {
  			if (data[i] == POOL_POISON_FREED)
  				continue;
  			if (pool->dev)
  				dev_err(pool->dev,
5835f2511   Hiroshige Sato   mm: Fix printk ty...
346
347
  					"dma_pool_alloc %s, %p (corrupted)
  ",
5de55b265   Matthieu CASTET   dmapool: make DMA...
348
349
  					pool->name, retval);
  			else
5835f2511   Hiroshige Sato   mm: Fix printk ty...
350
351
  				pr_err("dma_pool_alloc %s, %p (corrupted)
  ",
5de55b265   Matthieu CASTET   dmapool: make DMA...
352
353
354
355
356
357
358
359
360
361
362
  					pool->name, retval);
  
  			/*
  			 * Dump the first 4 bytes even if they are not
  			 * POOL_POISON_FREED
  			 */
  			print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 16, 1,
  					data, pool->size, 1);
  			break;
  		}
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
363
  	memset(retval, POOL_POISON_ALLOCATED, pool->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  #endif
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
365
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
  	return retval;
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
368
  EXPORT_SYMBOL(dma_pool_alloc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369

e87aa7737   Matthew Wilcox   dmapool: Fix styl...
370
  static struct dma_page *pool_find_page(struct dma_pool *pool, dma_addr_t dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
372
  	struct dma_page *page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
377
  	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...
378
  			return page;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	}
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
380
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
384
385
386
387
388
389
390
  /**
   * 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...
391
  void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  {
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
393
394
  	struct dma_page *page;
  	unsigned long flags;
a35a34551   Matthew Wilcox   Change dmapool fr...
395
  	unsigned int offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396

84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
397
  	spin_lock_irqsave(&pool->lock, flags);
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
398
399
  	page = pool_find_page(pool, dma);
  	if (!page) {
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
400
  		spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
  		if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
402
403
404
405
  			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
406
  		else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
407
408
409
  			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
410
411
  		return;
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
412
  	offset = vaddr - page->vaddr;
b5ee5befa   Andi Kleen   dmapool: enable d...
413
  #ifdef	DMAPOOL_DEBUG
a35a34551   Matthew Wilcox   Change dmapool fr...
414
  	if ((dma - page->dma) != offset) {
84bc227d7   Rolf Eike Beer   mm/dmapool.c: tak...
415
  		spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  		if (pool->dev)
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
417
418
419
420
  			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
421
  		else
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
422
423
424
425
  			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
426
427
  		return;
  	}
a35a34551   Matthew Wilcox   Change dmapool fr...
428
429
430
431
432
433
434
  	{
  		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...
435
  			spin_unlock_irqrestore(&pool->lock, flags);
a35a34551   Matthew Wilcox   Change dmapool fr...
436
437
438
439
440
441
442
443
444
445
446
447
  			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
448
  	}
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
449
  	memset(vaddr, POOL_POISON_FREED, pool->size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  	page->in_use--;
a35a34551   Matthew Wilcox   Change dmapool fr...
452
453
  	*(int *)vaddr = page->offset;
  	page->offset = offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
  	/*
  	 * Resist a temptation to do
a35a34551   Matthew Wilcox   Change dmapool fr...
456
  	 *    if (!is_page_busy(page)) pool_free_page(pool, page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  	 * Better have a few empty pages hang around.
  	 */
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
459
  	spin_unlock_irqrestore(&pool->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
461
  EXPORT_SYMBOL(dma_pool_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462

9ac7849e3   Tejun Heo   devres: device re...
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  /*
   * 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...
506
  EXPORT_SYMBOL(dmam_pool_create);
9ac7849e3   Tejun Heo   devres: device re...
507
508
509
510
511
512
513
514
515
516
  
  /**
   * 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;
172cb4b3d   Andy Shevchenko   mm/dmapool.c: reu...
517
  	WARN_ON(devres_release(dev, dmam_pool_release, dmam_pool_match, pool));
9ac7849e3   Tejun Heo   devres: device re...
518
  }
e87aa7737   Matthew Wilcox   dmapool: Fix styl...
519
  EXPORT_SYMBOL(dmam_pool_destroy);