diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-07-11 16:28:19 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-10-03 13:12:48 +1000 |
commit | 496734bf0391f38c196e16dbbfaaeda8e6ec5845 (patch) | |
tree | eaf17ad25eb57a101d1265a452f6b399882d9919 /drivers/gpu/drm/nouveau/core/core/mm.c | |
parent | 5a5c7432bbbd2e318dff107b4ff960ab543a7cef (diff) | |
download | linux-3.10-496734bf0391f38c196e16dbbfaaeda8e6ec5845.tar.gz linux-3.10-496734bf0391f38c196e16dbbfaaeda8e6ec5845.tar.bz2 linux-3.10-496734bf0391f38c196e16dbbfaaeda8e6ec5845.zip |
drm/nouveau/core: add support for reverse mm allocations
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/core/core/mm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/core/core/mm.c | 173 |
1 files changed, 122 insertions, 51 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index bf8d4a5461c..bfddf87926d 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -1,5 +1,5 @@ /* - * Copyright 2010 Red Hat Inc. + * Copyright 2012 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -22,19 +22,52 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/mm.h> +#include "core/os.h" +#include "core/mm.h" -static inline void -region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a) +#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ + list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) + +void +nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis) { - list_del(&a->nl_entry); - list_del(&a->fl_entry); - kfree(a); + struct nouveau_mm_node *this = *pthis; + + if (this) { + struct nouveau_mm_node *prev = node(this, prev); + struct nouveau_mm_node *next = node(this, next); + + if (prev && prev->type == 0) { + prev->length += this->length; + list_del(&this->nl_entry); + kfree(this); this = prev; + } + + if (next && next->type == 0) { + next->offset = this->offset; + next->length += this->length; + if (this->type == 0) + list_del(&this->fl_entry); + list_del(&this->nl_entry); + kfree(this); this = NULL; + } + + if (this && this->type != 0) { + list_for_each_entry(prev, &mm->free, fl_entry) { + if (this->offset < prev->offset) + break; + } + + list_add_tail(&this->fl_entry, &prev->fl_entry); + this->type = 0; + } + } + + *pthis = NULL; } static struct nouveau_mm_node * -region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) +region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) { struct nouveau_mm_node *b; @@ -56,38 +89,12 @@ region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) return b; } -#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ - list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) - -void -nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this) -{ - struct nouveau_mm_node *prev = node(this, prev); - struct nouveau_mm_node *next = node(this, next); - - list_add(&this->fl_entry, &mm->free); - this->type = 0; - - if (prev && prev->type == 0) { - prev->length += this->length; - region_put(mm, this); - this = prev; - } - - if (next && next->type == 0) { - next->offset = this->offset; - next->length += this->length; - region_put(mm, this); - } -} - int -nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc, - u32 align, struct nouveau_mm_node **pnode) +nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, + u32 align, struct nouveau_mm_node **pnode) { struct nouveau_mm_node *prev, *this, *next; - u32 min = size_nc ? size_nc : size; - u32 align_mask = align - 1; + u32 mask = align - 1; u32 splitoff; u32 s, e; @@ -103,16 +110,86 @@ nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc, if (next && next->type != type) e = rounddown(e, mm->block_size); - s = (s + align_mask) & ~align_mask; - e &= ~align_mask; - if (s > e || e - s < min) + s = (s + mask) & ~mask; + e &= ~mask; + if (s > e || e - s < size_min) continue; splitoff = s - this->offset; - if (splitoff && !region_split(mm, this, splitoff)) + if (splitoff && !region_head(mm, this, splitoff)) + return -ENOMEM; + + this = region_head(mm, this, min(size_max, e - s)); + if (!this) + return -ENOMEM; + + this->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +static struct nouveau_mm_node * +region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size) +{ + struct nouveau_mm_node *b; + + if (a->length == size) + return a; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (unlikely(b == NULL)) + return NULL; + + a->length -= size; + b->offset = a->offset + a->length; + b->length = size; + b->type = a->type; + + list_add(&b->nl_entry, &a->nl_entry); + if (b->type == 0) + list_add(&b->fl_entry, &a->fl_entry); + return b; +} + +int +nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min, + u32 align, struct nouveau_mm_node **pnode) +{ + struct nouveau_mm_node *prev, *this, *next; + u32 mask = align - 1; + + list_for_each_entry_reverse(this, &mm->free, fl_entry) { + u32 e = this->offset + this->length; + u32 s = this->offset; + u32 c = 0, a; + + prev = node(this, prev); + if (prev && prev->type != type) + s = roundup(s, mm->block_size); + + next = node(this, next); + if (next && next->type != type) { + e = rounddown(e, mm->block_size); + c = next->offset - e; + } + + s = (s + mask) & ~mask; + a = e - s; + if (s > e || a < size_min) + continue; + + a = min(a, size_max); + s = (e - a) & ~mask; + c += (e - s) - a; + + if (c && !region_tail(mm, this, c)) return -ENOMEM; - this = region_split(mm, this, min(size, e - s)); + this = region_tail(mm, this, a); if (!this) return -ENOMEM; @@ -147,6 +224,7 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) list_add_tail(&node->nl_entry, &mm->nodes); list_add_tail(&node->fl_entry, &mm->free); mm->heap_nodes++; + mm->heap_size += length; return 0; } @@ -158,15 +236,8 @@ nouveau_mm_fini(struct nouveau_mm *mm) int nodes = 0; list_for_each_entry(node, &mm->nodes, nl_entry) { - if (nodes++ == mm->heap_nodes) { - printk(KERN_ERR "nouveau_mm in use at destroy time!\n"); - list_for_each_entry(node, &mm->nodes, nl_entry) { - printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", - node->type, node->offset, node->length); - } - WARN_ON(1); + if (nodes++ == mm->heap_nodes) return -EBUSY; - } } kfree(heap); |