mm.c revision 987eec10dd76624d0edacdc7ecc7e1a6fc877373
1573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs/*
2573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * Copyright 2010 Red Hat Inc.
3573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs *
4573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * copy of this software and associated documentation files (the "Software"),
6573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * to deal in the Software without restriction, including without limitation
7573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * Software is furnished to do so, subject to the following conditions:
10573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs *
11573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * The above copyright notice and this permission notice shall be included in
12573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * all copies or substantial portions of the Software.
13573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs *
14573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs *
22573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs * Authors: Ben Skeggs
23573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs */
24573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
25573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs#include "drmP.h"
26573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs#include "nouveau_drv.h"
27573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs#include "nouveau_mm.h"
28573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
29573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsstatic inline void
30987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsregion_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
31573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
32573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	list_del(&a->nl_entry);
33573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	list_del(&a->fl_entry);
34573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	kfree(a);
35573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
36573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
37573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsstatic struct nouveau_mm_node *
38987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsregion_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
39573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
40573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	struct nouveau_mm_node *b;
41573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
42573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	if (a->length == size)
43573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		return a;
44573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
45573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	b = kmalloc(sizeof(*b), GFP_KERNEL);
46573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	if (unlikely(b == NULL))
47573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		return NULL;
48573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
49573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	b->offset = a->offset;
50573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	b->length = size;
51573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	b->type   = a->type;
52573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	a->offset += size;
53573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	a->length -= size;
54573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	list_add_tail(&b->nl_entry, &a->nl_entry);
558b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	if (b->type == 0)
56573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		list_add_tail(&b->fl_entry, &a->fl_entry);
57573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	return b;
58573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
59573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
60987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
618b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
62573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
63573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsvoid
64987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsnouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
65573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
668b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	struct nouveau_mm_node *prev = node(this, prev);
678b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	struct nouveau_mm_node *next = node(this, next);
68573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
69987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	list_add(&this->fl_entry, &mm->free);
708b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	this->type = 0;
71573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
728b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	if (prev && prev->type == 0) {
738b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		prev->length += this->length;
74987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		region_put(mm, this);
758b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		this = prev;
76573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	}
77573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
788b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	if (next && next->type == 0) {
798b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		next->offset  = this->offset;
808b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		next->length += this->length;
81987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		region_put(mm, this);
82573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	}
83573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
84573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
85573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsint
86987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsnouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
87573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	       u32 align, struct nouveau_mm_node **pnode)
88573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
898b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	struct nouveau_mm_node *prev, *this, *next;
908b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	u32 min = size_nc ? size_nc : size;
918b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	u32 align_mask = align - 1;
928b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	u32 splitoff;
938b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs	u32 s, e;
948b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs
95987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	list_for_each_entry(this, &mm->free, fl_entry) {
968b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		e = this->offset + this->length;
978b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		s = this->offset;
988b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs
998b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		prev = node(this, prev);
1008b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		if (prev && prev->type != type)
101987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs			s = roundup(s, mm->block_size);
1028b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs
1038b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		next = node(this, next);
1048b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		if (next && next->type != type)
105987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs			e = rounddown(e, mm->block_size);
1068b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs
1078b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		s  = (s + align_mask) & ~align_mask;
1088b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		e &= ~align_mask;
1098b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		if (s > e || e - s < min)
110573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs			continue;
111573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
1128b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		splitoff = s - this->offset;
113987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		if (splitoff && !region_split(mm, this, splitoff))
1148b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs			return -ENOMEM;
115573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
116987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		this = region_split(mm, this, min(size, e - s));
1178b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		if (!this)
118573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs			return -ENOMEM;
119573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
1208b464bfed674fc25d39d8a686010ebe509c8f62aBen Skeggs		this->type = type;
121573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		list_del(&this->fl_entry);
122573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		*pnode = this;
123573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		return 0;
124573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	}
125573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
126ef1b287169cd3d1e428c8ed8222e0bbf733d5dbbBen Skeggs	return -ENOSPC;
127573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
128573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
129573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsint
130987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsnouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
131573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
132573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	struct nouveau_mm_node *heap;
133573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
134573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	heap = kzalloc(sizeof(*heap), GFP_KERNEL);
135573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	if (!heap)
136573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		return -ENOMEM;
137573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	heap->offset = roundup(offset, block);
138573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	heap->length = rounddown(offset + length, block) - heap->offset;
139573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
140987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	mutex_init(&mm->mutex);
141987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	mm->block_size = block;
142987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	INIT_LIST_HEAD(&mm->nodes);
143987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	INIT_LIST_HEAD(&mm->free);
144987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs
145987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	list_add(&heap->nl_entry, &mm->nodes);
146987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	list_add(&heap->fl_entry, &mm->free);
147573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	return 0;
148573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
149573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
150573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggsint
151987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggsnouveau_mm_fini(struct nouveau_mm *mm)
152573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs{
153ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs	struct nouveau_mm_node *node, *heap =
154987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry);
155573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
156987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs	if (!list_is_singular(&mm->nodes)) {
157ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs		printk(KERN_ERR "nouveau_mm not empty at destroy time!\n");
158987eec10dd76624d0edacdc7ecc7e1a6fc877373Ben Skeggs		list_for_each_entry(node, &mm->nodes, nl_entry) {
159ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs			printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
160ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs			       node->type, node->offset, node->length);
161ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs		}
162ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs		WARN_ON(1);
163573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs		return -EBUSY;
164ad9ac437a500f8c0822bd5fe139af8ee2c132e15Ben Skeggs	}
165573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs
166573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	kfree(heap);
167573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs	return 0;
168573a2a37e8648a3249426c816f51e7ef50f6f73eBen Skeggs}
169