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