linux/drivers/gpu/drm/nouveau/core/core/mm.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24
  25#include "core/os.h"
  26#include "core/mm.h"
  27
  28#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
  29        list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
  30
  31void
  32nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
  33{
  34        struct nouveau_mm_node *this = *pthis;
  35
  36        if (this) {
  37                struct nouveau_mm_node *prev = node(this, prev);
  38                struct nouveau_mm_node *next = node(this, next);
  39
  40                if (prev && prev->type == 0) {
  41                        prev->length += this->length;
  42                        list_del(&this->nl_entry);
  43                        kfree(this); this = prev;
  44                }
  45
  46                if (next && next->type == 0) {
  47                        next->offset  = this->offset;
  48                        next->length += this->length;
  49                        if (this->type == 0)
  50                                list_del(&this->fl_entry);
  51                        list_del(&this->nl_entry);
  52                        kfree(this); this = NULL;
  53                }
  54
  55                if (this && this->type != 0) {
  56                        list_for_each_entry(prev, &mm->free, fl_entry) {
  57                                if (this->offset < prev->offset)
  58                                        break;
  59                        }
  60
  61                        list_add_tail(&this->fl_entry, &prev->fl_entry);
  62                        this->type = 0;
  63                }
  64        }
  65
  66        *pthis = NULL;
  67}
  68
  69static struct nouveau_mm_node *
  70region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
  71{
  72        struct nouveau_mm_node *b;
  73
  74        if (a->length == size)
  75                return a;
  76
  77        b = kmalloc(sizeof(*b), GFP_KERNEL);
  78        if (unlikely(b == NULL))
  79                return NULL;
  80
  81        b->offset = a->offset;
  82        b->length = size;
  83        b->type   = a->type;
  84        a->offset += size;
  85        a->length -= size;
  86        list_add_tail(&b->nl_entry, &a->nl_entry);
  87        if (b->type == 0)
  88                list_add_tail(&b->fl_entry, &a->fl_entry);
  89        return b;
  90}
  91
  92int
  93nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
  94                u32 align, struct nouveau_mm_node **pnode)
  95{
  96        struct nouveau_mm_node *prev, *this, *next;
  97        u32 mask = align - 1;
  98        u32 splitoff;
  99        u32 s, e;
 100
 101        BUG_ON(!type);
 102
 103        list_for_each_entry(this, &mm->free, fl_entry) {
 104                e = this->offset + this->length;
 105                s = this->offset;
 106
 107                prev = node(this, prev);
 108                if (prev && prev->type != type)
 109                        s = roundup(s, mm->block_size);
 110
 111                next = node(this, next);
 112                if (next && next->type != type)
 113                        e = rounddown(e, mm->block_size);
 114
 115                s  = (s + mask) & ~mask;
 116                e &= ~mask;
 117                if (s > e || e - s < size_min)
 118                        continue;
 119
 120                splitoff = s - this->offset;
 121                if (splitoff && !region_head(mm, this, splitoff))
 122                        return -ENOMEM;
 123
 124                this = region_head(mm, this, min(size_max, e - s));
 125                if (!this)
 126                        return -ENOMEM;
 127
 128                this->type = type;
 129                list_del(&this->fl_entry);
 130                *pnode = this;
 131                return 0;
 132        }
 133
 134        return -ENOSPC;
 135}
 136
 137static struct nouveau_mm_node *
 138region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
 139{
 140        struct nouveau_mm_node *b;
 141
 142        if (a->length == size)
 143                return a;
 144
 145        b = kmalloc(sizeof(*b), GFP_KERNEL);
 146        if (unlikely(b == NULL))
 147                return NULL;
 148
 149        a->length -= size;
 150        b->offset  = a->offset + a->length;
 151        b->length  = size;
 152        b->type    = a->type;
 153
 154        list_add(&b->nl_entry, &a->nl_entry);
 155        if (b->type == 0)
 156                list_add(&b->fl_entry, &a->fl_entry);
 157        return b;
 158}
 159
 160int
 161nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
 162                u32 align, struct nouveau_mm_node **pnode)
 163{
 164        struct nouveau_mm_node *prev, *this, *next;
 165        u32 mask = align - 1;
 166
 167        BUG_ON(!type);
 168
 169        list_for_each_entry_reverse(this, &mm->free, fl_entry) {
 170                u32 e = this->offset + this->length;
 171                u32 s = this->offset;
 172                u32 c = 0, a;
 173
 174                prev = node(this, prev);
 175                if (prev && prev->type != type)
 176                        s = roundup(s, mm->block_size);
 177
 178                next = node(this, next);
 179                if (next && next->type != type) {
 180                        e = rounddown(e, mm->block_size);
 181                        c = next->offset - e;
 182                }
 183
 184                s = (s + mask) & ~mask;
 185                a = e - s;
 186                if (s > e || a < size_min)
 187                        continue;
 188
 189                a  = min(a, size_max);
 190                s  = (e - a) & ~mask;
 191                c += (e - s) - a;
 192
 193                if (c && !region_tail(mm, this, c))
 194                        return -ENOMEM;
 195
 196                this = region_tail(mm, this, a);
 197                if (!this)
 198                        return -ENOMEM;
 199
 200                this->type = type;
 201                list_del(&this->fl_entry);
 202                *pnode = this;
 203                return 0;
 204        }
 205
 206        return -ENOSPC;
 207}
 208
 209int
 210nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
 211{
 212        struct nouveau_mm_node *node;
 213
 214        if (block) {
 215                INIT_LIST_HEAD(&mm->nodes);
 216                INIT_LIST_HEAD(&mm->free);
 217                mm->block_size = block;
 218                mm->heap_nodes = 0;
 219        }
 220
 221        node = kzalloc(sizeof(*node), GFP_KERNEL);
 222        if (!node)
 223                return -ENOMEM;
 224
 225        if (length) {
 226                node->offset  = roundup(offset, mm->block_size);
 227                node->length  = rounddown(offset + length, mm->block_size);
 228                node->length -= node->offset;
 229        }
 230
 231        list_add_tail(&node->nl_entry, &mm->nodes);
 232        list_add_tail(&node->fl_entry, &mm->free);
 233        mm->heap_nodes++;
 234        return 0;
 235}
 236
 237int
 238nouveau_mm_fini(struct nouveau_mm *mm)
 239{
 240        if (nouveau_mm_initialised(mm)) {
 241                struct nouveau_mm_node *node, *heap =
 242                        list_first_entry(&mm->nodes, typeof(*heap), nl_entry);
 243                int nodes = 0;
 244
 245                list_for_each_entry(node, &mm->nodes, nl_entry) {
 246                        if (WARN_ON(nodes++ == mm->heap_nodes))
 247                                return -EBUSY;
 248                }
 249
 250                kfree(heap);
 251        }
 252
 253        return 0;
 254}
 255