11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*- 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 30d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie/* 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * All Rights Reserved. 6bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * 7bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * Permission is hereby granted, free of charge, to any person obtaining a 8bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * copy of this software and associated documentation files (the 9bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * "Software"), to deal in the Software without restriction, including 10bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * without limitation the rights to use, copy, modify, merge, publish, 11bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * distribute, sub license, and/or sell copies of the Software, and to 12bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * permit persons to whom the Software is furnished to do so, subject to 13bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * the following conditions: 14bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * 15bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * The above copyright notice and this permission notice (including the 16bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * next paragraph) shall be included in all copies or substantial portions 17bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * of the Software. 18bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * 19bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 23bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26bc54fd1ad3c5972be339a08528ab631326ed2b38Dave Airlie * 270d6aa60b4ac9689b750e35cd66f5d7c053aff0f4Dave Airlie */ 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "drmP.h" 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "drm.h" 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "i915_drm.h" 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "i915_drv.h" 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This memory manager is integrated into the global/local lru 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mechanisms used by the clients. Specifically, it operates by 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * setting the 'in_use' fields of the global LRU to indicate whether 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this region is privately allocated to a client. 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This does require the client to actually respect that field. 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Currently no effort is made to allocate 'private' memory in any 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * clever way - the LRU information isn't used to determine which 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * block to allocate, and the ring is drained prior to allocations -- 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in other words allocation is expensive. 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4684b1fd103dbbe01b5905db1444d3fc8afa9a7207Dave Airliestatic void mark_block(struct drm_device * dev, struct mem_block *p, int in_use) 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds drm_i915_private_t *dev_priv = dev->dev_private; 497c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; 507c1c2871a6a3a114853ec6836e9035ac1c0c7f7aDave Airlie drm_i915_sarea_t *sarea_priv = master_priv->sarea_priv; 51c60ce623bd16137627009d05e311d877729f2ad6Dave Airlie struct drm_tex_region *list; 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned shift, nr; 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned start; 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned end; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned i; 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int age; 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds shift = dev_priv->tex_lru_log_granularity; 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nr = I915_NR_TEX_REGIONS; 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds start = p->start >> shift; 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds end = (p->start + p->size - 1) >> shift; 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds age = ++sarea_priv->texAge; 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list = sarea_priv->texList; 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Mark the regions with the new flag and update their age. Move 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * them to head of list to preserve LRU semantics. 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = start; i <= end; i++) { 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[i].in_use = in_use; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[i].age = age; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* remove_from_list(i) 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[(unsigned)list[i].next].prev = list[i].prev; 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[(unsigned)list[i].prev].next = list[i].next; 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* insert_at_head(list, i) 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[i].prev = nr; 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[i].next = list[nr].next; 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[(unsigned)list[nr].next].prev = i; 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list[nr].next = i; 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Very simple allocator for agp memory, working on a static range 89b5e89ed53ed8d24f83ba1941c07382af00ed238eDave Airlie * already mapped into each client's address space. 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mem_block *split_block(struct mem_block *p, int start, int size, 936c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt struct drm_file *file_priv) 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maybe cut off the start of an existing block */ 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (start > p->start) { 979a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt struct mem_block *newblock = kmalloc(sizeof(*newblock), 989a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt GFP_KERNEL); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newblock) 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->start = start; 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->size = p->size - (start - p->start); 1036c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt newblock->file_priv = NULL; 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->next = p->next; 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->prev = p; 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next->prev = newblock; 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next = newblock; 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->size -= newblock->size; 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p = newblock; 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maybe cut off the end of an existing block */ 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size < p->size) { 1149a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt struct mem_block *newblock = kmalloc(sizeof(*newblock), 1159a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt GFP_KERNEL); 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!newblock) 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->start = start + size; 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->size = p->size - size; 1206c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt newblock->file_priv = NULL; 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->next = p->next; 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds newblock->prev = p; 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next->prev = newblock; 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next = newblock; 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->size = size; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out: 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Our block is in the middle */ 1306c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt p->file_priv = file_priv; 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return p; 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mem_block *alloc_block(struct mem_block *heap, int size, 1356c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt int align2, struct drm_file *file_priv) 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *p; 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int mask = (1 << align2) - 1; 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (p = heap->next; p != heap; p = p->next) { 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int start = (p->start + mask) & ~mask; 1426c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt if (p->file_priv == NULL && start + size <= p->start + p->size) 1436c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt return split_block(p, start, size, file_priv); 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mem_block *find_block(struct mem_block *heap, int start) 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *p; 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (p = heap->next; p != heap; p = p->next) 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (p->start == start) 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return p; 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void free_block(struct mem_block *p) 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1626c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt p->file_priv = NULL; 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1646c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt /* Assumes a single contiguous range. Needs a special file_priv in 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'heap' to stop it being subsumed. 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1676c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt if (p->next->file_priv == NULL) { 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *q = p->next; 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->size += q->size; 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next = q->next; 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next->prev = p; 1729a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(q); 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1756c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt if (p->prev->file_priv == NULL) { 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *q = p->prev; 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds q->size += p->size; 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds q->next = p->next; 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds q->next->prev = q; 1809a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(p); 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Initialize. How to check for an uninitialized heap? 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int init_heap(struct mem_block **heap, int start, int size) 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1889a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL); 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!blocks) 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1939a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt *heap = kmalloc(sizeof(**heap), GFP_KERNEL); 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!*heap) { 1959a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(blocks); 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds blocks->start = start; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds blocks->size = size; 2016c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt blocks->file_priv = NULL; 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds blocks->next = blocks->prev = *heap; 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(*heap, 0, sizeof(**heap)); 2050206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi (*heap)->file_priv = (struct drm_file *) -1; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (*heap)->next = (*heap)->prev = blocks; 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Free all blocks associated with the releasing file. 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2126c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholtvoid i915_mem_release(struct drm_device * dev, struct drm_file *file_priv, 2136c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt struct mem_block *heap) 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *p; 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!heap || !heap->next) 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (p = heap->next; p != heap; p = p->next) { 2216c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt if (p->file_priv == file_priv) { 2226c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt p->file_priv = NULL; 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mark_block(dev, p, 0); 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2276c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt /* Assumes a single contiguous range. Needs a special file_priv in 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'heap' to stop it being subsumed. 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (p = heap->next; p != heap; p = p->next) { 2316c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt while (p->file_priv == NULL && p->next->file_priv == NULL) { 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *q = p->next; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->size += q->size; 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next = q->next; 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p->next->prev = p; 2369a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(q); 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Shutdown. 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid i915_mem_takedown(struct mem_block **heap) 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *p; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!*heap) 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (p = (*heap)->next; p != *heap;) { 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *q = p; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds p = p->next; 2539a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(q); 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2569a298b2acd771d8a5c0004d8f8e4156c65b11f6bEric Anholt kfree(*heap); 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *heap = NULL; 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region) 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (region) { 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case I915_MEM_REGION_AGP: 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return &dev_priv->agp_heap; 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* IOCTL HANDLERS */ 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 272c153f45f9b7e30289157bba3ff5682291df16caaEric Anholtint i915_mem_alloc(struct drm_device *dev, void *data, 273c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt struct drm_file *file_priv) 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds drm_i915_private_t *dev_priv = dev->dev_private; 276c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt drm_i915_mem_alloc_t *alloc = data; 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *block, **heap; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev_priv) { 2803e684eae586a9b210a4517da5637a255b1ff5a92Márton Németh DRM_ERROR("called with no initialization\n"); 28120caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EINVAL; 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 284c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt heap = get_heap(dev_priv, alloc->region); 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!heap || !*heap) 28620caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Make things easier on ourselves: all allocations at least 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4k aligned. 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 291c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt if (alloc->alignment < 12) 292c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt alloc->alignment = 12; 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 294c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv); 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!block) 29720caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -ENOMEM; 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mark_block(dev, block, 1); 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt if (DRM_COPY_TO_USER(alloc->region_offset, &block->start, 302c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt sizeof(int))) { 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DRM_ERROR("copy_to_user\n"); 30420caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 310c153f45f9b7e30289157bba3ff5682291df16caaEric Anholtint i915_mem_free(struct drm_device *dev, void *data, 311c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt struct drm_file *file_priv) 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds drm_i915_private_t *dev_priv = dev->dev_private; 314c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt drm_i915_mem_free_t *memfree = data; 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block *block, **heap; 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev_priv) { 3183e684eae586a9b210a4517da5637a255b1ff5a92Márton Németh DRM_ERROR("called with no initialization\n"); 31920caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EINVAL; 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 322c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt heap = get_heap(dev_priv, memfree->region); 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!heap || !*heap) 32420caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 326c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt block = find_block(*heap, memfree->region_offset); 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!block) 32820caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3306c340eac0285f3d62406d2d902d0e96fbf2a5dc0Eric Anholt if (block->file_priv != file_priv) 33120caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EPERM; 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mark_block(dev, block, 0); 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_block(block); 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 338c153f45f9b7e30289157bba3ff5682291df16caaEric Anholtint i915_mem_init_heap(struct drm_device *dev, void *data, 339c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt struct drm_file *file_priv) 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds drm_i915_private_t *dev_priv = dev->dev_private; 342c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt drm_i915_mem_init_heap_t *initheap = data; 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mem_block **heap; 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!dev_priv) { 3463e684eae586a9b210a4517da5637a255b1ff5a92Márton Németh DRM_ERROR("called with no initialization\n"); 34720caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EINVAL; 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 350c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt heap = get_heap(dev_priv, initheap->region); 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!heap) 35220caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (*heap) { 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DRM_ERROR("heap already initialized?"); 35620caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 359c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt return init_heap(heap, initheap->start, initheap->size); 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 361de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie 3620206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshiint i915_mem_destroy_heap(struct drm_device *dev, void *data, 3630206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi struct drm_file *file_priv) 364de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie{ 365de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie drm_i915_private_t *dev_priv = dev->dev_private; 366c153f45f9b7e30289157bba3ff5682291df16caaEric Anholt drm_i915_mem_destroy_heap_t *destroyheap = data; 367de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie struct mem_block **heap; 368de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie 3690206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi if (!dev_priv) { 3700206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi DRM_ERROR("called with no initialization\n"); 37120caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EINVAL; 372de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie } 373de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie 3740206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi heap = get_heap(dev_priv, destroyheap->region); 375de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie if (!heap) { 376de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie DRM_ERROR("get_heap failed"); 37720caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 378de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie } 379bc5f4523f772cc7629c5c5a46cf4f2a07a5500b8Dave Airlie 380de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie if (!*heap) { 381de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie DRM_ERROR("heap not initialized?"); 38220caafa6ecb2487d9b223aa33e7cc704f912a758Eric Anholt return -EFAULT; 383de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie } 384de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie 3850206e353a0416ad63ce07f53c807c2c725633b87Akshay Joshi i915_mem_takedown(heap); 386de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie return 0; 387de227f5f32775d86e5c780a7cffdd2e08574f7fbDave Airlie} 388