|
@@ -42,36 +42,131 @@
|
|
|
*/
|
|
|
|
|
|
#include "drmP.h"
|
|
|
+#include <linux/slab.h>
|
|
|
+
|
|
|
+unsigned long drm_mm_tail_space(drm_mm_t *mm)
|
|
|
+{
|
|
|
+ struct list_head *tail_node;
|
|
|
+ drm_mm_node_t *entry;
|
|
|
+
|
|
|
+ tail_node = mm->ml_entry.prev;
|
|
|
+ entry = list_entry(tail_node, drm_mm_node_t, ml_entry);
|
|
|
+ if (!entry->free)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return entry->size;
|
|
|
+}
|
|
|
+
|
|
|
+int drm_mm_remove_space_from_tail(drm_mm_t *mm, unsigned long size)
|
|
|
+{
|
|
|
+ struct list_head *tail_node;
|
|
|
+ drm_mm_node_t *entry;
|
|
|
+
|
|
|
+ tail_node = mm->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_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_mm_add_space_to_tail(drm_mm_t *mm, unsigned long size)
|
|
|
+{
|
|
|
+ struct list_head *tail_node;
|
|
|
+ drm_mm_node_t *entry;
|
|
|
+
|
|
|
+ tail_node = mm->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_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;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
|
|
|
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 = 0;
|
|
|
|
|
|
if (alignment)
|
|
|
- size += alignment - 1;
|
|
|
+ tmp = parent->start % 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_alloc(sizeof(*child), DRM_MEM_MM);
|
|
|
- if (!child)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&child->ml_entry);
|
|
|
- INIT_LIST_HEAD(&child->fl_entry);
|
|
|
+ child = drm_mm_split_at_start(parent, size);
|
|
|
+ }
|
|
|
|
|
|
- child->free = 0;
|
|
|
- child->size = size;
|
|
|
- child->start = parent->start;
|
|
|
+ if (align_splitoff)
|
|
|
+ drm_mm_put_block(align_splitoff);
|
|
|
|
|
|
- list_add_tail(&child->ml_entry, &parent->ml_entry);
|
|
|
- parent->size -= size;
|
|
|
- parent->start += size;
|
|
|
- }
|
|
|
return child;
|
|
|
}
|
|
|
|
|
@@ -80,12 +175,12 @@ drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent,
|
|
|
* Otherwise add to the free stack.
|
|
|
*/
|
|
|
|
|
|
-void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur)
|
|
|
+void drm_mm_put_block(drm_mm_node_t * cur)
|
|
|
{
|
|
|
|
|
|
- drm_mm_node_t *list_root = &mm->root_node;
|
|
|
+ drm_mm_t *mm = cur->mm;
|
|
|
struct list_head *cur_head = &cur->ml_entry;
|
|
|
- struct list_head *root_head = &list_root->ml_entry;
|
|
|
+ struct list_head *root_head = &mm->ml_entry;
|
|
|
drm_mm_node_t *prev_node = NULL;
|
|
|
drm_mm_node_t *next_node;
|
|
|
|
|
@@ -116,7 +211,7 @@ void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur)
|
|
|
}
|
|
|
if (!merged) {
|
|
|
cur->free = 1;
|
|
|
- list_add(&cur->fl_entry, &list_root->fl_entry);
|
|
|
+ list_add(&cur->fl_entry, &mm->fl_entry);
|
|
|
} else {
|
|
|
list_del(&cur->ml_entry);
|
|
|
drm_free(cur, sizeof(*cur), DRM_MEM_MM);
|
|
@@ -128,20 +223,30 @@ drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm,
|
|
|
unsigned alignment, int best_match)
|
|
|
{
|
|
|
struct list_head *list;
|
|
|
- const struct list_head *free_stack = &mm->root_node.fl_entry;
|
|
|
+ const struct list_head *free_stack = &mm->fl_entry;
|
|
|
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 (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) {
|
|
@@ -154,40 +259,32 @@ drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm,
|
|
|
return best;
|
|
|
}
|
|
|
|
|
|
-int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size)
|
|
|
+int drm_mm_clean(drm_mm_t * mm)
|
|
|
{
|
|
|
- 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_alloc(sizeof(*child), DRM_MEM_MM);
|
|
|
- if (!child)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- INIT_LIST_HEAD(&child->ml_entry);
|
|
|
- INIT_LIST_HEAD(&child->fl_entry);
|
|
|
+ struct list_head *head = &mm->ml_entry;
|
|
|
|
|
|
- child->start = start;
|
|
|
- child->size = size;
|
|
|
- child->free = 1;
|
|
|
+ return (head->next->next == head);
|
|
|
+}
|
|
|
|
|
|
- list_add(&child->fl_entry, &mm->root_node.fl_entry);
|
|
|
- list_add(&child->ml_entry, &mm->root_node.ml_entry);
|
|
|
+int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size)
|
|
|
+{
|
|
|
+ INIT_LIST_HEAD(&mm->ml_entry);
|
|
|
+ INIT_LIST_HEAD(&mm->fl_entry);
|
|
|
|
|
|
- return 0;
|
|
|
+ return drm_mm_create_tail_node(mm, start, size);
|
|
|
}
|
|
|
|
|
|
EXPORT_SYMBOL(drm_mm_init);
|
|
|
|
|
|
void drm_mm_takedown(drm_mm_t * mm)
|
|
|
{
|
|
|
- struct list_head *bnode = mm->root_node.fl_entry.next;
|
|
|
+ struct list_head *bnode = mm->fl_entry.next;
|
|
|
drm_mm_node_t *entry;
|
|
|
|
|
|
entry = list_entry(bnode, drm_mm_node_t, fl_entry);
|
|
|
|
|
|
- if (entry->ml_entry.next != &mm->root_node.ml_entry ||
|
|
|
- entry->fl_entry.next != &mm->root_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;
|
|
|
}
|