|
@@ -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);
|