From 3ad8db2071d30c198403e605f2726fc5c3e46bfd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 28 Apr 2008 16:54:53 -0700 Subject: Rename drm_mm.c and its fuctions to drm_memrange. It's not really a graphics memory allocator, just something to track ranges of address space. It doesn't involve actual allocation, and was consuming some desired namespace. --- linux-core/drm_memrange.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 linux-core/drm_memrange.c (limited to 'linux-core/drm_memrange.c') diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c new file mode 100644 index 00000000..0ae03655 --- /dev/null +++ b/linux-core/drm_memrange.c @@ -0,0 +1,296 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + **************************************************************************/ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström + */ + +#include "drmP.h" +#include + +unsigned long drm_memrange_tail_space(struct drm_memrange *mm) +{ + struct list_head *tail_node; + struct drm_memrange_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_memrange_node, ml_entry); + if (!entry->free) + return 0; + + return entry->size; +} + +int drm_memrange_remove_space_from_tail(struct drm_memrange *mm, unsigned long size) +{ + struct list_head *tail_node; + struct drm_memrange_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_memrange_node, ml_entry); + if (!entry->free) + return -ENOMEM; + + if (entry->size <= size) + return -ENOMEM; + + entry->size -= size; + return 0; +} + + +static int drm_memrange_create_tail_node(struct drm_memrange *mm, + unsigned long start, + unsigned long size) +{ + struct drm_memrange_node *child; + + child = (struct drm_memrange_node *) + drm_ctl_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return -ENOMEM; + + child->free = 1; + child->size = size; + child->start = start; + child->mm = mm; + + list_add_tail(&child->ml_entry, &mm->ml_entry); + list_add_tail(&child->fl_entry, &mm->fl_entry); + + return 0; +} + + +int drm_memrange_add_space_to_tail(struct drm_memrange *mm, unsigned long size) +{ + struct list_head *tail_node; + struct drm_memrange_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_memrange_node, ml_entry); + if (!entry->free) { + return drm_memrange_create_tail_node(mm, entry->start + entry->size, size); + } + entry->size += size; + return 0; +} + +static struct drm_memrange_node *drm_memrange_split_at_start(struct drm_memrange_node *parent, + unsigned long size) +{ + struct drm_memrange_node *child; + + child = (struct drm_memrange_node *) + drm_ctl_alloc(sizeof(*child), DRM_MEM_MM); + if (!child) + return NULL; + + INIT_LIST_HEAD(&child->fl_entry); + + child->free = 0; + child->size = size; + child->start = parent->start; + child->mm = parent->mm; + + list_add_tail(&child->ml_entry, &parent->ml_entry); + INIT_LIST_HEAD(&child->fl_entry); + + parent->size -= size; + parent->start += size; + return child; +} + +struct drm_memrange_node *drm_memrange_get_block(struct drm_memrange_node * parent, + unsigned long size, unsigned alignment) +{ + + struct drm_memrange_node *align_splitoff = NULL; + struct drm_memrange_node *child; + unsigned tmp = 0; + + if (alignment) + tmp = parent->start % alignment; + + if (tmp) { + align_splitoff = drm_memrange_split_at_start(parent, alignment - tmp); + if (!align_splitoff) + return NULL; + } + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = 0; + return parent; + } else { + child = drm_memrange_split_at_start(parent, size); + } + + if (align_splitoff) + drm_memrange_put_block(align_splitoff); + + return child; +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void drm_memrange_put_block(struct drm_memrange_node * cur) +{ + + struct drm_memrange *mm = cur->mm; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &mm->ml_entry; + struct drm_memrange_node *prev_node = NULL; + struct drm_memrange_node *next_node; + + int merged = 0; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, struct drm_memrange_node, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = 1; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, struct drm_memrange_node, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_ctl_free(next_node, sizeof(*next_node), + DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = 1; + } + } + } + if (!merged) { + cur->free = 1; + list_add(&cur->fl_entry, &mm->fl_entry); + } else { + list_del(&cur->ml_entry); + drm_ctl_free(cur, sizeof(*cur), DRM_MEM_MM); + } +} +EXPORT_SYMBOL(drm_memrange_put_block); + +struct drm_memrange_node *drm_memrange_search_free(const struct drm_memrange * mm, + unsigned long size, + unsigned alignment, int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->fl_entry; + struct drm_memrange_node *entry; + struct drm_memrange_node *best; + unsigned long best_size; + unsigned wasted; + + best = NULL; + best_size = ~0UL; + + list_for_each(list, free_stack) { + entry = list_entry(list, struct drm_memrange_node, fl_entry); + wasted = 0; + + if (entry->size < size) + continue; + + if (alignment) { + register unsigned tmp = entry->start % alignment; + if (tmp) + wasted += alignment - tmp; + } + + + if (entry->size >= size + wasted) { + if (!best_match) + return entry; + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return best; +} + +int drm_memrange_clean(struct drm_memrange * mm) +{ + struct list_head *head = &mm->ml_entry; + + return (head->next->next == head); +} + +int drm_memrange_init(struct drm_memrange * mm, unsigned long start, unsigned long size) +{ + INIT_LIST_HEAD(&mm->ml_entry); + INIT_LIST_HEAD(&mm->fl_entry); + + return drm_memrange_create_tail_node(mm, start, size); +} + +EXPORT_SYMBOL(drm_memrange_init); + +void drm_memrange_takedown(struct drm_memrange * mm) +{ + struct list_head *bnode = mm->fl_entry.next; + struct drm_memrange_node *entry; + + entry = list_entry(bnode, struct drm_memrange_node, fl_entry); + + if (entry->ml_entry.next != &mm->ml_entry || + entry->fl_entry.next != &mm->fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + drm_ctl_free(entry, sizeof(*entry), DRM_MEM_MM); +} + +EXPORT_SYMBOL(drm_memrange_takedown); -- cgit v1.2.3 From 1a8406795052e3ec49e400465f3211d04fd9dd86 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 30 Apr 2008 16:03:15 -0700 Subject: Hacking towards hooking up execbuffer. --- linux-core/drm_memrange.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'linux-core/drm_memrange.c') diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c index 0ae03655..e1d2233b 100644 --- a/linux-core/drm_memrange.c +++ b/linux-core/drm_memrange.c @@ -273,6 +273,31 @@ int drm_memrange_init(struct drm_memrange * mm, unsigned long start, unsigned lo return drm_memrange_create_tail_node(mm, start, size); } +/** + * Walks the list of allocated memory ranges and calls the callback on + * one. + */ +int drm_memrange_for_each(struct drm_memrange *mm, + int (*callback)(struct drm_memrange_node *node, + void *data), + void *data) +{ + struct list_head *list, *next; + + list_for_each_safe(list, next, &mm->ml_entry) { + struct drm_memrange_node *cur; + int ret; + + cur = list_entry(list, struct drm_memrange_node, ml_entry); + + ret = callback(cur, data); + if (ret != 0) + return ret; + } + + return 0; +} + EXPORT_SYMBOL(drm_memrange_init); void drm_memrange_takedown(struct drm_memrange * mm) -- cgit v1.2.3 From ccd1bae0f676490a88240c62f02e072d2cf3b030 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 1 May 2008 15:22:21 -0700 Subject: checkpoint: relocations support. --- linux-core/drm_memrange.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'linux-core/drm_memrange.c') diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c index e1d2233b..663943ab 100644 --- a/linux-core/drm_memrange.c +++ b/linux-core/drm_memrange.c @@ -167,6 +167,7 @@ struct drm_memrange_node *drm_memrange_get_block(struct drm_memrange_node * pare return child; } +EXPORT_SYMBOL(drm_memrange_get_block); /* * Put a block. Merge with the previous and / or next block if they are free. @@ -257,6 +258,7 @@ struct drm_memrange_node *drm_memrange_search_free(const struct drm_memrange * m return best; } +EXPORT_SYMBOL(drm_memrange_search_free); int drm_memrange_clean(struct drm_memrange * mm) { @@ -297,6 +299,7 @@ int drm_memrange_for_each(struct drm_memrange *mm, return 0; } +EXPORT_SYMBOL(drm_memrange_for_each); EXPORT_SYMBOL(drm_memrange_init); -- cgit v1.2.3 From afe574f328fca42f2fa5fbc1c7a1c13d0f35d2f6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 2 May 2008 17:49:52 -0700 Subject: Don't include the tail guard memrange in foreach callbacking. --- linux-core/drm_memrange.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'linux-core/drm_memrange.c') diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c index 663943ab..7304cbaa 100644 --- a/linux-core/drm_memrange.c +++ b/linux-core/drm_memrange.c @@ -291,10 +291,11 @@ int drm_memrange_for_each(struct drm_memrange *mm, int ret; cur = list_entry(list, struct drm_memrange_node, ml_entry); - - ret = callback(cur, data); - if (ret != 0) - return ret; + if (!cur->free) { + ret = callback(cur, data); + if (ret != 0) + return ret; + } } return 0; -- cgit v1.2.3 From dafe48e6239a4e9b49dd87b8c70224e8eeeb6079 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 5 May 2008 14:38:04 -0700 Subject: GEM: Replace drm_memrange_for_each with just evicting what we brought in. I was wrong about how the data structure worked, and didn't care to fix it to support debugging code. --- linux-core/drm_memrange.c | 27 --------------------------- 1 file changed, 27 deletions(-) (limited to 'linux-core/drm_memrange.c') diff --git a/linux-core/drm_memrange.c b/linux-core/drm_memrange.c index 7304cbaa..7014c4e2 100644 --- a/linux-core/drm_memrange.c +++ b/linux-core/drm_memrange.c @@ -275,33 +275,6 @@ int drm_memrange_init(struct drm_memrange * mm, unsigned long start, unsigned lo return drm_memrange_create_tail_node(mm, start, size); } -/** - * Walks the list of allocated memory ranges and calls the callback on - * one. - */ -int drm_memrange_for_each(struct drm_memrange *mm, - int (*callback)(struct drm_memrange_node *node, - void *data), - void *data) -{ - struct list_head *list, *next; - - list_for_each_safe(list, next, &mm->ml_entry) { - struct drm_memrange_node *cur; - int ret; - - cur = list_entry(list, struct drm_memrange_node, ml_entry); - if (!cur->free) { - ret = callback(cur, data); - if (ret != 0) - return ret; - } - } - - return 0; -} -EXPORT_SYMBOL(drm_memrange_for_each); - EXPORT_SYMBOL(drm_memrange_init); void drm_memrange_takedown(struct drm_memrange * mm) -- cgit v1.2.3