Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
radeon_mem.c
Go to the documentation of this file.
1 /* radeon_mem.c -- Simple GART/fb memory manager for radeon -*- linux-c -*- */
2 /*
3  * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
4  *
5  * The Weather Channel (TM) funded Tungsten Graphics to develop the
6  * initial release of the Radeon 8500 driver under the XFree86 license.
7  * This notice must be preserved.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice (including the next
17  * paragraph) shall be included in all copies or substantial portions of the
18  * Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  *
28  * Authors:
29  * Keith Whitwell <[email protected]>
30  */
31 
32 #include <drm/drmP.h>
33 #include <drm/radeon_drm.h>
34 #include "radeon_drv.h"
35 
36 /* Very simple allocator for GART memory, working on a static range
37  * already mapped into each client's address space.
38  */
39 
40 static struct mem_block *split_block(struct mem_block *p, int start, int size,
41  struct drm_file *file_priv)
42 {
43  /* Maybe cut off the start of an existing block */
44  if (start > p->start) {
45  struct mem_block *newblock = kmalloc(sizeof(*newblock),
46  GFP_KERNEL);
47  if (!newblock)
48  goto out;
49  newblock->start = start;
50  newblock->size = p->size - (start - p->start);
51  newblock->file_priv = NULL;
52  newblock->next = p->next;
53  newblock->prev = p;
54  p->next->prev = newblock;
55  p->next = newblock;
56  p->size -= newblock->size;
57  p = newblock;
58  }
59 
60  /* Maybe cut off the end of an existing block */
61  if (size < p->size) {
62  struct mem_block *newblock = kmalloc(sizeof(*newblock),
63  GFP_KERNEL);
64  if (!newblock)
65  goto out;
66  newblock->start = start + size;
67  newblock->size = p->size - size;
68  newblock->file_priv = NULL;
69  newblock->next = p->next;
70  newblock->prev = p;
71  p->next->prev = newblock;
72  p->next = newblock;
73  p->size = size;
74  }
75 
76  out:
77  /* Our block is in the middle */
78  p->file_priv = file_priv;
79  return p;
80 }
81 
82 static struct mem_block *alloc_block(struct mem_block *heap, int size,
83  int align2, struct drm_file *file_priv)
84 {
85  struct mem_block *p;
86  int mask = (1 << align2) - 1;
87 
88  list_for_each(p, heap) {
89  int start = (p->start + mask) & ~mask;
90  if (p->file_priv == NULL && start + size <= p->start + p->size)
91  return split_block(p, start, size, file_priv);
92  }
93 
94  return NULL;
95 }
96 
97 static struct mem_block *find_block(struct mem_block *heap, int start)
98 {
99  struct mem_block *p;
100 
101  list_for_each(p, heap)
102  if (p->start == start)
103  return p;
104 
105  return NULL;
106 }
107 
108 static void free_block(struct mem_block *p)
109 {
110  p->file_priv = NULL;
111 
112  /* Assumes a single contiguous range. Needs a special file_priv in
113  * 'heap' to stop it being subsumed.
114  */
115  if (p->next->file_priv == NULL) {
116  struct mem_block *q = p->next;
117  p->size += q->size;
118  p->next = q->next;
119  p->next->prev = p;
120  kfree(q);
121  }
122 
123  if (p->prev->file_priv == NULL) {
124  struct mem_block *q = p->prev;
125  q->size += p->size;
126  q->next = p->next;
127  q->next->prev = q;
128  kfree(p);
129  }
130 }
131 
132 /* Initialize. How to check for an uninitialized heap?
133  */
134 static int init_heap(struct mem_block **heap, int start, int size)
135 {
136  struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
137 
138  if (!blocks)
139  return -ENOMEM;
140 
141  *heap = kzalloc(sizeof(**heap), GFP_KERNEL);
142  if (!*heap) {
143  kfree(blocks);
144  return -ENOMEM;
145  }
146 
147  blocks->start = start;
148  blocks->size = size;
149  blocks->file_priv = NULL;
150  blocks->next = blocks->prev = *heap;
151 
152  (*heap)->file_priv = (struct drm_file *) - 1;
153  (*heap)->next = (*heap)->prev = blocks;
154  return 0;
155 }
156 
157 /* Free all blocks associated with the releasing file.
158  */
159 void radeon_mem_release(struct drm_file *file_priv, struct mem_block *heap)
160 {
161  struct mem_block *p;
162 
163  if (!heap || !heap->next)
164  return;
165 
166  list_for_each(p, heap) {
167  if (p->file_priv == file_priv)
168  p->file_priv = NULL;
169  }
170 
171  /* Assumes a single contiguous range. Needs a special file_priv in
172  * 'heap' to stop it being subsumed.
173  */
174  list_for_each(p, heap) {
175  while (p->file_priv == NULL && p->next->file_priv == NULL) {
176  struct mem_block *q = p->next;
177  p->size += q->size;
178  p->next = q->next;
179  p->next->prev = p;
180  kfree(q);
181  }
182  }
183 }
184 
185 /* Shutdown.
186  */
187 void radeon_mem_takedown(struct mem_block **heap)
188 {
189  struct mem_block *p;
190 
191  if (!*heap)
192  return;
193 
194  for (p = (*heap)->next; p != *heap;) {
195  struct mem_block *q = p;
196  p = p->next;
197  kfree(q);
198  }
199 
200  kfree(*heap);
201  *heap = NULL;
202 }
203 
204 /* IOCTL HANDLERS */
205 
206 static struct mem_block **get_heap(drm_radeon_private_t * dev_priv, int region)
207 {
208  switch (region) {
210  return &dev_priv->gart_heap;
212  return &dev_priv->fb_heap;
213  default:
214  return NULL;
215  }
216 }
217 
218 int radeon_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
219 {
220  drm_radeon_private_t *dev_priv = dev->dev_private;
221  drm_radeon_mem_alloc_t *alloc = data;
222  struct mem_block *block, **heap;
223 
224  if (!dev_priv) {
225  DRM_ERROR("called with no initialization\n");
226  return -EINVAL;
227  }
228 
229  heap = get_heap(dev_priv, alloc->region);
230  if (!heap || !*heap)
231  return -EFAULT;
232 
233  /* Make things easier on ourselves: all allocations at least
234  * 4k aligned.
235  */
236  if (alloc->alignment < 12)
237  alloc->alignment = 12;
238 
239  block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv);
240 
241  if (!block)
242  return -ENOMEM;
243 
244  if (DRM_COPY_TO_USER(alloc->region_offset, &block->start,
245  sizeof(int))) {
246  DRM_ERROR("copy_to_user\n");
247  return -EFAULT;
248  }
249 
250  return 0;
251 }
252 
253 int radeon_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
254 {
255  drm_radeon_private_t *dev_priv = dev->dev_private;
256  drm_radeon_mem_free_t *memfree = data;
257  struct mem_block *block, **heap;
258 
259  if (!dev_priv) {
260  DRM_ERROR("called with no initialization\n");
261  return -EINVAL;
262  }
263 
264  heap = get_heap(dev_priv, memfree->region);
265  if (!heap || !*heap)
266  return -EFAULT;
267 
268  block = find_block(*heap, memfree->region_offset);
269  if (!block)
270  return -EFAULT;
271 
272  if (block->file_priv != file_priv)
273  return -EPERM;
274 
275  free_block(block);
276  return 0;
277 }
278 
279 int radeon_mem_init_heap(struct drm_device *dev, void *data, struct drm_file *file_priv)
280 {
281  drm_radeon_private_t *dev_priv = dev->dev_private;
282  drm_radeon_mem_init_heap_t *initheap = data;
283  struct mem_block **heap;
284 
285  if (!dev_priv) {
286  DRM_ERROR("called with no initialization\n");
287  return -EINVAL;
288  }
289 
290  heap = get_heap(dev_priv, initheap->region);
291  if (!heap)
292  return -EFAULT;
293 
294  if (*heap) {
295  DRM_ERROR("heap already initialized?");
296  return -EFAULT;
297  }
298 
299  return init_heap(heap, initheap->start, initheap->size);
300 }