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