]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/drm/i915_mem.c
This commit was generated by cvs2svn to compensate for changes in r147001,
[FreeBSD/FreeBSD.git] / sys / dev / drm / i915_mem.c
1 /* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*-
2  *
3  * $FreeBSD$
4  */
5 /**************************************************************************
6  *
7  * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
8  * All Rights Reserved.
9  *
10  **************************************************************************/
11
12 #include "drmP.h"
13 #include "drm.h"
14 #include "i915_drm.h"
15 #include "i915_drv.h"
16
17 /* This memory manager is integrated into the global/local lru
18  * mechanisms used by the clients.  Specifically, it operates by
19  * setting the 'in_use' fields of the global LRU to indicate whether
20  * this region is privately allocated to a client.
21  *
22  * This does require the client to actually respect that field.
23  *
24  * Currently no effort is made to allocate 'private' memory in any
25  * clever way - the LRU information isn't used to determine which
26  * block to allocate, and the ring is drained prior to allocations --
27  * in other words allocation is expensive.
28  */
29 static void mark_block(drm_device_t * dev, struct mem_block *p, int in_use)
30 {
31         drm_i915_private_t *dev_priv = dev->dev_private;
32         drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
33         drm_tex_region_t *list;
34         unsigned shift, nr;
35         unsigned start;
36         unsigned end;
37         unsigned i;
38         int age;
39
40         shift = dev_priv->tex_lru_log_granularity;
41         nr = I915_NR_TEX_REGIONS;
42
43         start = p->start >> shift;
44         end = (p->start + p->size - 1) >> shift;
45
46         age = ++sarea_priv->texAge;
47         list = sarea_priv->texList;
48
49         /* Mark the regions with the new flag and update their age.  Move
50          * them to head of list to preserve LRU semantics.
51          */
52         for (i = start; i <= end; i++) {
53                 list[i].in_use = in_use;
54                 list[i].age = age;
55
56                 /* remove_from_list(i)
57                  */
58                 list[(unsigned)list[i].next].prev = list[i].prev;
59                 list[(unsigned)list[i].prev].next = list[i].next;
60
61                 /* insert_at_head(list, i)
62                  */
63                 list[i].prev = nr;
64                 list[i].next = list[nr].next;
65                 list[(unsigned)list[nr].next].prev = i;
66                 list[nr].next = i;
67         }
68 }
69
70 /* Very simple allocator for agp memory, working on a static range
71  * already mapped into each client's address space.
72  */
73
74 static struct mem_block *split_block(struct mem_block *p, int start, int size,
75                                      DRMFILE filp)
76 {
77         /* Maybe cut off the start of an existing block */
78         if (start > p->start) {
79                 struct mem_block *newblock =
80                     drm_alloc(sizeof(*newblock), DRM_MEM_BUFLISTS);
81                 if (!newblock)
82                         goto out;
83                 newblock->start = start;
84                 newblock->size = p->size - (start - p->start);
85                 newblock->filp = NULL;
86                 newblock->next = p->next;
87                 newblock->prev = p;
88                 p->next->prev = newblock;
89                 p->next = newblock;
90                 p->size -= newblock->size;
91                 p = newblock;
92         }
93
94         /* Maybe cut off the end of an existing block */
95         if (size < p->size) {
96                 struct mem_block *newblock =
97                     drm_alloc(sizeof(*newblock), DRM_MEM_BUFLISTS);
98                 if (!newblock)
99                         goto out;
100                 newblock->start = start + size;
101                 newblock->size = p->size - size;
102                 newblock->filp = NULL;
103                 newblock->next = p->next;
104                 newblock->prev = p;
105                 p->next->prev = newblock;
106                 p->next = newblock;
107                 p->size = size;
108         }
109
110       out:
111         /* Our block is in the middle */
112         p->filp = filp;
113         return p;
114 }
115
116 static struct mem_block *alloc_block(struct mem_block *heap, int size,
117                                      int align2, DRMFILE filp)
118 {
119         struct mem_block *p;
120         int mask = (1 << align2) - 1;
121
122         for (p = heap->next; p != heap; p = p->next) {
123                 int start = (p->start + mask) & ~mask;
124                 if (p->filp == NULL && start + size <= p->start + p->size)
125                         return split_block(p, start, size, filp);
126         }
127
128         return NULL;
129 }
130
131 static struct mem_block *find_block(struct mem_block *heap, int start)
132 {
133         struct mem_block *p;
134
135         for (p = heap->next; p != heap; p = p->next)
136                 if (p->start == start)
137                         return p;
138
139         return NULL;
140 }
141
142 static void free_block(struct mem_block *p)
143 {
144         p->filp = NULL;
145
146         /* Assumes a single contiguous range.  Needs a special filp in
147          * 'heap' to stop it being subsumed.
148          */
149         if (p->next->filp == NULL) {
150                 struct mem_block *q = p->next;
151                 p->size += q->size;
152                 p->next = q->next;
153                 p->next->prev = p;
154                 drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
155         }
156
157         if (p->prev->filp == NULL) {
158                 struct mem_block *q = p->prev;
159                 q->size += p->size;
160                 q->next = p->next;
161                 q->next->prev = q;
162                 drm_free(p, sizeof(*q), DRM_MEM_BUFLISTS);
163         }
164 }
165
166 /* Initialize.  How to check for an uninitialized heap?
167  */
168 static int init_heap(struct mem_block **heap, int start, int size)
169 {
170         struct mem_block *blocks = drm_alloc(sizeof(*blocks), DRM_MEM_BUFLISTS);
171
172         if (!blocks)
173                 return -ENOMEM;
174
175         *heap = drm_alloc(sizeof(**heap), DRM_MEM_BUFLISTS);
176         if (!*heap) {
177                 drm_free(blocks, sizeof(*blocks), DRM_MEM_BUFLISTS);
178                 return -ENOMEM;
179         }
180
181         blocks->start = start;
182         blocks->size = size;
183         blocks->filp = NULL;
184         blocks->next = blocks->prev = *heap;
185
186         memset(*heap, 0, sizeof(**heap));
187         (*heap)->filp = (DRMFILE) - 1;
188         (*heap)->next = (*heap)->prev = blocks;
189         return 0;
190 }
191
192 /* Free all blocks associated with the releasing file.
193  */
194 void i915_mem_release(drm_device_t * dev, DRMFILE filp, struct mem_block *heap)
195 {
196         struct mem_block *p;
197
198         if (!heap || !heap->next)
199                 return;
200
201         for (p = heap->next; p != heap; p = p->next) {
202                 if (p->filp == filp) {
203                         p->filp = NULL;
204                         mark_block(dev, p, 0);
205                 }
206         }
207
208         /* Assumes a single contiguous range.  Needs a special filp in
209          * 'heap' to stop it being subsumed.
210          */
211         for (p = heap->next; p != heap; p = p->next) {
212                 while (p->filp == NULL && p->next->filp == NULL) {
213                         struct mem_block *q = p->next;
214                         p->size += q->size;
215                         p->next = q->next;
216                         p->next->prev = p;
217                         drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
218                 }
219         }
220 }
221
222 /* Shutdown.
223  */
224 void i915_mem_takedown(struct mem_block **heap)
225 {
226         struct mem_block *p;
227
228         if (!*heap)
229                 return;
230
231         for (p = (*heap)->next; p != *heap;) {
232                 struct mem_block *q = p;
233                 p = p->next;
234                 drm_free(q, sizeof(*q), DRM_MEM_BUFLISTS);
235         }
236
237         drm_free(*heap, sizeof(**heap), DRM_MEM_BUFLISTS);
238         *heap = NULL;
239 }
240
241 static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region)
242 {
243         switch (region) {
244         case I915_MEM_REGION_AGP:
245                 return &dev_priv->agp_heap;
246         default:
247                 return NULL;
248         }
249 }
250
251 /* IOCTL HANDLERS */
252
253 int i915_mem_alloc(DRM_IOCTL_ARGS)
254 {
255         DRM_DEVICE;
256         drm_i915_private_t *dev_priv = dev->dev_private;
257         drm_i915_mem_alloc_t alloc;
258         struct mem_block *block, **heap;
259
260         if (!dev_priv) {
261                 DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
262                 return DRM_ERR(EINVAL);
263         }
264
265         DRM_COPY_FROM_USER_IOCTL(alloc, (drm_i915_mem_alloc_t __user *) data,
266                                  sizeof(alloc));
267
268         heap = get_heap(dev_priv, alloc.region);
269         if (!heap || !*heap)
270                 return DRM_ERR(EFAULT);
271
272         /* Make things easier on ourselves: all allocations at least
273          * 4k aligned.
274          */
275         if (alloc.alignment < 12)
276                 alloc.alignment = 12;
277
278         block = alloc_block(*heap, alloc.size, alloc.alignment, filp);
279
280         if (!block)
281                 return DRM_ERR(ENOMEM);
282
283         mark_block(dev, block, 1);
284
285         if (DRM_COPY_TO_USER(alloc.region_offset, &block->start, sizeof(int))) {
286                 DRM_ERROR("copy_to_user\n");
287                 return DRM_ERR(EFAULT);
288         }
289
290         return 0;
291 }
292
293 int i915_mem_free(DRM_IOCTL_ARGS)
294 {
295         DRM_DEVICE;
296         drm_i915_private_t *dev_priv = dev->dev_private;
297         drm_i915_mem_free_t memfree;
298         struct mem_block *block, **heap;
299
300         if (!dev_priv) {
301                 DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
302                 return DRM_ERR(EINVAL);
303         }
304
305         DRM_COPY_FROM_USER_IOCTL(memfree, (drm_i915_mem_free_t __user *) data,
306                                  sizeof(memfree));
307
308         heap = get_heap(dev_priv, memfree.region);
309         if (!heap || !*heap)
310                 return DRM_ERR(EFAULT);
311
312         block = find_block(*heap, memfree.region_offset);
313         if (!block)
314                 return DRM_ERR(EFAULT);
315
316         if (block->filp != filp)
317                 return DRM_ERR(EPERM);
318
319         mark_block(dev, block, 0);
320         free_block(block);
321         return 0;
322 }
323
324 int i915_mem_init_heap(DRM_IOCTL_ARGS)
325 {
326         DRM_DEVICE;
327         drm_i915_private_t *dev_priv = dev->dev_private;
328         drm_i915_mem_init_heap_t initheap;
329         struct mem_block **heap;
330
331         if (!dev_priv) {
332                 DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
333                 return DRM_ERR(EINVAL);
334         }
335
336         DRM_COPY_FROM_USER_IOCTL(initheap,
337                                  (drm_i915_mem_init_heap_t __user *) data,
338                                  sizeof(initheap));
339
340         heap = get_heap(dev_priv, initheap.region);
341         if (!heap)
342                 return DRM_ERR(EFAULT);
343
344         if (*heap) {
345                 DRM_ERROR("heap already initialized?");
346                 return DRM_ERR(EFAULT);
347         }
348
349         return init_heap(heap, initheap.start, initheap.size);
350 }