Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpu/drm/i915/i915_mem.c
15113 views
1
/* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*-
2
*/
3
/*
4
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
5
* All Rights Reserved.
6
*
7
* Permission is hereby granted, free of charge, to any person obtaining a
8
* copy of this software and associated documentation files (the
9
* "Software"), to deal in the Software without restriction, including
10
* without limitation the rights to use, copy, modify, merge, publish,
11
* distribute, sub license, and/or sell copies of the Software, and to
12
* permit persons to whom the Software is furnished to do so, subject to
13
* the following conditions:
14
*
15
* The above copyright notice and this permission notice (including the
16
* next paragraph) shall be included in all copies or substantial portions
17
* of the Software.
18
*
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
*
27
*/
28
29
#include "drmP.h"
30
#include "drm.h"
31
#include "i915_drm.h"
32
#include "i915_drv.h"
33
34
/* This memory manager is integrated into the global/local lru
35
* mechanisms used by the clients. Specifically, it operates by
36
* setting the 'in_use' fields of the global LRU to indicate whether
37
* this region is privately allocated to a client.
38
*
39
* This does require the client to actually respect that field.
40
*
41
* Currently no effort is made to allocate 'private' memory in any
42
* clever way - the LRU information isn't used to determine which
43
* block to allocate, and the ring is drained prior to allocations --
44
* in other words allocation is expensive.
45
*/
46
static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)
47
{
48
drm_i915_private_t *dev_priv = dev->dev_private;
49
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
50
drm_i915_sarea_t *sarea_priv = master_priv->sarea_priv;
51
struct drm_tex_region *list;
52
unsigned shift, nr;
53
unsigned start;
54
unsigned end;
55
unsigned i;
56
int age;
57
58
shift = dev_priv->tex_lru_log_granularity;
59
nr = I915_NR_TEX_REGIONS;
60
61
start = p->start >> shift;
62
end = (p->start + p->size - 1) >> shift;
63
64
age = ++sarea_priv->texAge;
65
list = sarea_priv->texList;
66
67
/* Mark the regions with the new flag and update their age. Move
68
* them to head of list to preserve LRU semantics.
69
*/
70
for (i = start; i <= end; i++) {
71
list[i].in_use = in_use;
72
list[i].age = age;
73
74
/* remove_from_list(i)
75
*/
76
list[(unsigned)list[i].next].prev = list[i].prev;
77
list[(unsigned)list[i].prev].next = list[i].next;
78
79
/* insert_at_head(list, i)
80
*/
81
list[i].prev = nr;
82
list[i].next = list[nr].next;
83
list[(unsigned)list[nr].next].prev = i;
84
list[nr].next = i;
85
}
86
}
87
88
/* Very simple allocator for agp memory, working on a static range
89
* already mapped into each client's address space.
90
*/
91
92
static struct mem_block *split_block(struct mem_block *p, int start, int size,
93
struct drm_file *file_priv)
94
{
95
/* Maybe cut off the start of an existing block */
96
if (start > p->start) {
97
struct mem_block *newblock = kmalloc(sizeof(*newblock),
98
GFP_KERNEL);
99
if (!newblock)
100
goto out;
101
newblock->start = start;
102
newblock->size = p->size - (start - p->start);
103
newblock->file_priv = NULL;
104
newblock->next = p->next;
105
newblock->prev = p;
106
p->next->prev = newblock;
107
p->next = newblock;
108
p->size -= newblock->size;
109
p = newblock;
110
}
111
112
/* Maybe cut off the end of an existing block */
113
if (size < p->size) {
114
struct mem_block *newblock = kmalloc(sizeof(*newblock),
115
GFP_KERNEL);
116
if (!newblock)
117
goto out;
118
newblock->start = start + size;
119
newblock->size = p->size - size;
120
newblock->file_priv = NULL;
121
newblock->next = p->next;
122
newblock->prev = p;
123
p->next->prev = newblock;
124
p->next = newblock;
125
p->size = size;
126
}
127
128
out:
129
/* Our block is in the middle */
130
p->file_priv = file_priv;
131
return p;
132
}
133
134
static struct mem_block *alloc_block(struct mem_block *heap, int size,
135
int align2, struct drm_file *file_priv)
136
{
137
struct mem_block *p;
138
int mask = (1 << align2) - 1;
139
140
for (p = heap->next; p != heap; p = p->next) {
141
int start = (p->start + mask) & ~mask;
142
if (p->file_priv == NULL && start + size <= p->start + p->size)
143
return split_block(p, start, size, file_priv);
144
}
145
146
return NULL;
147
}
148
149
static struct mem_block *find_block(struct mem_block *heap, int start)
150
{
151
struct mem_block *p;
152
153
for (p = heap->next; p != heap; p = p->next)
154
if (p->start == start)
155
return p;
156
157
return NULL;
158
}
159
160
static void free_block(struct mem_block *p)
161
{
162
p->file_priv = NULL;
163
164
/* Assumes a single contiguous range. Needs a special file_priv in
165
* 'heap' to stop it being subsumed.
166
*/
167
if (p->next->file_priv == NULL) {
168
struct mem_block *q = p->next;
169
p->size += q->size;
170
p->next = q->next;
171
p->next->prev = p;
172
kfree(q);
173
}
174
175
if (p->prev->file_priv == NULL) {
176
struct mem_block *q = p->prev;
177
q->size += p->size;
178
q->next = p->next;
179
q->next->prev = q;
180
kfree(p);
181
}
182
}
183
184
/* Initialize. How to check for an uninitialized heap?
185
*/
186
static int init_heap(struct mem_block **heap, int start, int size)
187
{
188
struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
189
190
if (!blocks)
191
return -ENOMEM;
192
193
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
194
if (!*heap) {
195
kfree(blocks);
196
return -ENOMEM;
197
}
198
199
blocks->start = start;
200
blocks->size = size;
201
blocks->file_priv = NULL;
202
blocks->next = blocks->prev = *heap;
203
204
memset(*heap, 0, sizeof(**heap));
205
(*heap)->file_priv = (struct drm_file *) - 1;
206
(*heap)->next = (*heap)->prev = blocks;
207
return 0;
208
}
209
210
/* Free all blocks associated with the releasing file.
211
*/
212
void i915_mem_release(struct drm_device * dev, struct drm_file *file_priv,
213
struct mem_block *heap)
214
{
215
struct mem_block *p;
216
217
if (!heap || !heap->next)
218
return;
219
220
for (p = heap->next; p != heap; p = p->next) {
221
if (p->file_priv == file_priv) {
222
p->file_priv = NULL;
223
mark_block(dev, p, 0);
224
}
225
}
226
227
/* Assumes a single contiguous range. Needs a special file_priv in
228
* 'heap' to stop it being subsumed.
229
*/
230
for (p = heap->next; p != heap; p = p->next) {
231
while (p->file_priv == NULL && p->next->file_priv == NULL) {
232
struct mem_block *q = p->next;
233
p->size += q->size;
234
p->next = q->next;
235
p->next->prev = p;
236
kfree(q);
237
}
238
}
239
}
240
241
/* Shutdown.
242
*/
243
void i915_mem_takedown(struct mem_block **heap)
244
{
245
struct mem_block *p;
246
247
if (!*heap)
248
return;
249
250
for (p = (*heap)->next; p != *heap;) {
251
struct mem_block *q = p;
252
p = p->next;
253
kfree(q);
254
}
255
256
kfree(*heap);
257
*heap = NULL;
258
}
259
260
static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region)
261
{
262
switch (region) {
263
case I915_MEM_REGION_AGP:
264
return &dev_priv->agp_heap;
265
default:
266
return NULL;
267
}
268
}
269
270
/* IOCTL HANDLERS */
271
272
int i915_mem_alloc(struct drm_device *dev, void *data,
273
struct drm_file *file_priv)
274
{
275
drm_i915_private_t *dev_priv = dev->dev_private;
276
drm_i915_mem_alloc_t *alloc = data;
277
struct mem_block *block, **heap;
278
279
if (!dev_priv) {
280
DRM_ERROR("called with no initialization\n");
281
return -EINVAL;
282
}
283
284
heap = get_heap(dev_priv, alloc->region);
285
if (!heap || !*heap)
286
return -EFAULT;
287
288
/* Make things easier on ourselves: all allocations at least
289
* 4k aligned.
290
*/
291
if (alloc->alignment < 12)
292
alloc->alignment = 12;
293
294
block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv);
295
296
if (!block)
297
return -ENOMEM;
298
299
mark_block(dev, block, 1);
300
301
if (DRM_COPY_TO_USER(alloc->region_offset, &block->start,
302
sizeof(int))) {
303
DRM_ERROR("copy_to_user\n");
304
return -EFAULT;
305
}
306
307
return 0;
308
}
309
310
int i915_mem_free(struct drm_device *dev, void *data,
311
struct drm_file *file_priv)
312
{
313
drm_i915_private_t *dev_priv = dev->dev_private;
314
drm_i915_mem_free_t *memfree = data;
315
struct mem_block *block, **heap;
316
317
if (!dev_priv) {
318
DRM_ERROR("called with no initialization\n");
319
return -EINVAL;
320
}
321
322
heap = get_heap(dev_priv, memfree->region);
323
if (!heap || !*heap)
324
return -EFAULT;
325
326
block = find_block(*heap, memfree->region_offset);
327
if (!block)
328
return -EFAULT;
329
330
if (block->file_priv != file_priv)
331
return -EPERM;
332
333
mark_block(dev, block, 0);
334
free_block(block);
335
return 0;
336
}
337
338
int i915_mem_init_heap(struct drm_device *dev, void *data,
339
struct drm_file *file_priv)
340
{
341
drm_i915_private_t *dev_priv = dev->dev_private;
342
drm_i915_mem_init_heap_t *initheap = data;
343
struct mem_block **heap;
344
345
if (!dev_priv) {
346
DRM_ERROR("called with no initialization\n");
347
return -EINVAL;
348
}
349
350
heap = get_heap(dev_priv, initheap->region);
351
if (!heap)
352
return -EFAULT;
353
354
if (*heap) {
355
DRM_ERROR("heap already initialized?");
356
return -EFAULT;
357
}
358
359
return init_heap(heap, initheap->start, initheap->size);
360
}
361
362
int i915_mem_destroy_heap( struct drm_device *dev, void *data,
363
struct drm_file *file_priv )
364
{
365
drm_i915_private_t *dev_priv = dev->dev_private;
366
drm_i915_mem_destroy_heap_t *destroyheap = data;
367
struct mem_block **heap;
368
369
if ( !dev_priv ) {
370
DRM_ERROR( "called with no initialization\n" );
371
return -EINVAL;
372
}
373
374
heap = get_heap( dev_priv, destroyheap->region );
375
if (!heap) {
376
DRM_ERROR("get_heap failed");
377
return -EFAULT;
378
}
379
380
if (!*heap) {
381
DRM_ERROR("heap not initialized?");
382
return -EFAULT;
383
}
384
385
i915_mem_takedown( heap );
386
return 0;
387
}
388
389