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 */ 46static 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 92static 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 134static 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 149static 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 160static 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 */ 186static 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 */ 212void 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 */ 243void 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 260static 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 272int 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 310int 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 338int 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 362int 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