summaryrefslogtreecommitdiff
path: root/linux-core/drm_mm.c
diff options
context:
space:
mode:
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2006-10-26 21:17:43 +0200
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2006-10-26 21:17:43 +0200
commit47dbfc4e4a3e8ce2ec468bc3874f74f7e2b13476 (patch)
tree84231b2f839577e0486bd3920f0c6a809103a5cb /linux-core/drm_mm.c
parentb4fba1679b6156e3ca6f053b44cae0b003febe7f (diff)
Add improved alignment functionality to the core memory manager.
This makes an allocated block actually align itself and returns any wasted space to the manager. Also add some functions to grow and shrink the managed area. This will be used in the future to manage the buffer object swap cache.
Diffstat (limited to 'linux-core/drm_mm.c')
-rw-r--r--linux-core/drm_mm.c171
1 files changed, 126 insertions, 45 deletions
diff --git a/linux-core/drm_mm.c b/linux-core/drm_mm.c
index 4af33bde..5e0ba5e4 100644
--- a/linux-core/drm_mm.c
+++ b/linux-core/drm_mm.c
@@ -44,39 +44,132 @@
#include "drmP.h"
#include <linux/slab.h>
-drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent,
- unsigned long size, unsigned alignment)
+unsigned long tail_space(drm_mm_t *mm)
{
+ struct list_head *tail_node;
+ drm_mm_node_t *entry;
+
+ tail_node = mm->root_node.ml_entry.prev;
+ entry = list_entry(tail_node, drm_mm_node_t, ml_entry);
+ if (!entry->free)
+ return 0;
+
+ return entry->size;
+}
+
+int remove_space_from_tail(drm_mm_t *mm, unsigned long size)
+{
+ struct list_head *tail_node;
+ drm_mm_node_t *entry;
+
+ tail_node = mm->root_node.ml_entry.prev;
+ entry = list_entry(tail_node, drm_mm_node_t, ml_entry);
+ if (!entry->free)
+ return -ENOMEM;
+
+ if (entry->size <= size)
+ return -ENOMEM;
+ entry->size -= size;
+ return 0;
+}
+
+
+static int drm_mm_create_tail_node(drm_mm_t *mm,
+ unsigned long start,
+ unsigned long size)
+{
drm_mm_node_t *child;
+
+ child = (drm_mm_node_t *)
+ drm_ctl_cache_alloc(drm_cache.mm, sizeof(*child),
+ GFP_KERNEL);
+ if (!child)
+ return -ENOMEM;
- if (alignment)
- size += alignment - 1;
+ child->free = 1;
+ child->size = size;
+ child->start = start;
+ child->mm = mm;
+
+ list_add_tail(&child->ml_entry, &mm->root_node.ml_entry);
+ list_add_tail(&child->fl_entry, &mm->root_node.fl_entry);
+
+ return 0;
+}
+
+
+int add_space_to_tail(drm_mm_t *mm, unsigned long size)
+{
+ struct list_head *tail_node;
+ drm_mm_node_t *entry;
+
+ tail_node = mm->root_node.ml_entry.prev;
+ entry = list_entry(tail_node, drm_mm_node_t, ml_entry);
+ if (!entry->free) {
+ return drm_mm_create_tail_node(mm, entry->start + entry->size, size);
+ }
+ entry->size += size;
+ return 0;
+}
+
+static drm_mm_node_t *drm_mm_split_at_start(drm_mm_node_t *parent,
+ unsigned long size)
+{
+ drm_mm_node_t *child;
+
+ child = (drm_mm_node_t *)
+ drm_ctl_cache_alloc(drm_cache.mm, sizeof(*child),
+ GFP_KERNEL);
+ 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;
+}
+
+
+
+drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent,
+ unsigned long size, unsigned alignment)
+{
+
+ drm_mm_node_t *align_splitoff = NULL;
+ drm_mm_node_t *child;
+ unsigned tmp = size % alignment;
+
+ if (tmp) {
+ align_splitoff = drm_mm_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_mm_node_t *)
- drm_ctl_cache_alloc(drm_cache.mm, sizeof(*child),
- GFP_KERNEL);
- if (!child)
+ child = drm_mm_split_at_start(parent, size);
+ if (!child) {
+ if (align_splitoff)
+ drm_mm_put_block(align_splitoff);
return NULL;
-
- INIT_LIST_HEAD(&child->ml_entry);
- 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);
- parent->size -= size;
- parent->start += size;
+ }
}
+ if (align_splitoff)
+ drm_mm_put_block(align_splitoff);
+
return child;
}
@@ -139,16 +232,23 @@ drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm,
drm_mm_node_t *entry;
drm_mm_node_t *best;
unsigned long best_size;
+ unsigned wasted;
best = NULL;
best_size = ~0UL;
- if (alignment)
- size += alignment - 1;
-
list_for_each(list, free_stack) {
entry = list_entry(list, drm_mm_node_t, fl_entry);
- if (entry->size >= size) {
+ wasted = 0;
+
+ if (alignment) {
+ register unsigned tmp = size % alignment;
+ if (tmp)
+ wasted += alignment - tmp;
+ }
+
+
+ if (entry->size >= size + wasted) {
if (!best_match)
return entry;
if (size < best_size) {
@@ -170,29 +270,10 @@ int drm_mm_clean(drm_mm_t * mm)
int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size)
{
- drm_mm_node_t *child;
-
INIT_LIST_HEAD(&mm->root_node.ml_entry);
INIT_LIST_HEAD(&mm->root_node.fl_entry);
- child = (drm_mm_node_t *)
- drm_ctl_cache_alloc(drm_cache.mm, sizeof(*child), GFP_KERNEL);
-
- if (!child)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&child->ml_entry);
- INIT_LIST_HEAD(&child->fl_entry);
-
- child->start = start;
- child->size = size;
- child->free = 1;
- child->mm = mm;
-
- list_add(&child->fl_entry, &mm->root_node.fl_entry);
- list_add(&child->ml_entry, &mm->root_node.ml_entry);
-
- return 0;
+ return drm_mm_create_tail_node(mm, start, size);
}
EXPORT_SYMBOL(drm_mm_init);