#include "drmP.h"
#include "drm_mm.h"
#include <linux/slab.h>
+#include <linux/seq_file.h>
#define MM_UNUSED_TARGET 4
return child;
}
+/* drm_mm_pre_get() - pre allocate drm_mm_node structure
+ * drm_mm: memory manager struct we are pre-allocating for
+ *
+ * Returns 0 on success or -ENOMEM if allocation fails.
+ */
int drm_mm_pre_get(struct drm_mm *mm)
{
struct drm_mm_node *node;
}
-
-struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *node,
- unsigned long size, unsigned alignment)
+struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
+ unsigned long size,
+ unsigned alignment,
+ int atomic)
{
struct drm_mm_node *align_splitoff = NULL;
if (tmp) {
align_splitoff =
- drm_mm_split_at_start(node, alignment - tmp, 0);
+ drm_mm_split_at_start(node, alignment - tmp, atomic);
if (unlikely(align_splitoff == NULL))
return NULL;
}
list_del_init(&node->fl_entry);
node->free = 0;
} else {
- node = drm_mm_split_at_start(node, size, 0);
+ node = drm_mm_split_at_start(node, size, atomic);
}
if (align_splitoff)
return node;
}
-
-EXPORT_SYMBOL(drm_mm_get_block);
-
-struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent,
- unsigned long size,
- unsigned alignment)
+EXPORT_SYMBOL(drm_mm_get_block_generic);
+
+struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node,
+ unsigned long size,
+ unsigned alignment,
+ unsigned long start,
+ unsigned long end,
+ int atomic)
{
-
struct drm_mm_node *align_splitoff = NULL;
- struct drm_mm_node *child;
unsigned tmp = 0;
+ unsigned wasted = 0;
+ if (node->start < start)
+ wasted += start - node->start;
if (alignment)
- tmp = parent->start % alignment;
+ tmp = ((node->start + wasted) % alignment);
- if (tmp) {
- align_splitoff =
- drm_mm_split_at_start(parent, alignment - tmp, 1);
+ if (tmp)
+ wasted += alignment - tmp;
+ if (wasted) {
+ align_splitoff = drm_mm_split_at_start(node, wasted, atomic);
if (unlikely(align_splitoff == NULL))
return NULL;
}
- if (parent->size == size) {
- list_del_init(&parent->fl_entry);
- parent->free = 0;
- return parent;
+ if (node->size == size) {
+ list_del_init(&node->fl_entry);
+ node->free = 0;
} else {
- child = drm_mm_split_at_start(parent, size, 1);
+ node = drm_mm_split_at_start(node, size, atomic);
}
if (align_splitoff)
drm_mm_put_block(align_splitoff);
- return child;
+ return node;
}
-EXPORT_SYMBOL(drm_mm_get_block_atomic);
+EXPORT_SYMBOL(drm_mm_get_block_range_generic);
/*
* Put a block. Merge with the previous and / or next block if they are free.
prev_node->size += next_node->size;
list_del(&next_node->ml_entry);
list_del(&next_node->fl_entry);
+ spin_lock(&mm->unused_lock);
if (mm->num_unused < MM_UNUSED_TARGET) {
list_add(&next_node->fl_entry,
&mm->unused_nodes);
++mm->num_unused;
} else
kfree(next_node);
+ spin_unlock(&mm->unused_lock);
} else {
next_node->size += cur->size;
next_node->start = cur->start;
list_add(&cur->fl_entry, &mm->fl_entry);
} else {
list_del(&cur->ml_entry);
+ spin_lock(&mm->unused_lock);
if (mm->num_unused < MM_UNUSED_TARGET) {
list_add(&cur->fl_entry, &mm->unused_nodes);
++mm->num_unused;
} else
kfree(cur);
+ spin_unlock(&mm->unused_lock);
}
}
if (entry->size >= size + wasted) {
if (!best_match)
return entry;
- if (size < best_size) {
+ if (entry->size < best_size) {
best = entry;
best_size = entry->size;
}
}
EXPORT_SYMBOL(drm_mm_search_free);
+struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
+ unsigned long size,
+ unsigned alignment,
+ unsigned long start,
+ unsigned long end,
+ int best_match)
+{
+ struct list_head *list;
+ const struct list_head *free_stack = &mm->fl_entry;
+ struct drm_mm_node *entry;
+ struct drm_mm_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_mm_node, fl_entry);
+ wasted = 0;
+
+ if (entry->size < size)
+ continue;
+
+ if (entry->start > end || (entry->start+entry->size) < start)
+ continue;
+
+ if (entry->start < start)
+ wasted += start - entry->start;
+
+ if (alignment) {
+ register unsigned tmp = (entry->start + wasted) % alignment;
+ if (tmp)
+ wasted += alignment - tmp;
+ }
+
+ if (entry->size >= size + wasted &&
+ (entry->start + wasted + size) <= end) {
+ if (!best_match)
+ return entry;
+ if (entry->size < best_size) {
+ best = entry;
+ best_size = entry->size;
+ }
+ }
+ }
+
+ return best;
+}
+EXPORT_SYMBOL(drm_mm_search_free_in_range);
+
int drm_mm_clean(struct drm_mm * mm)
{
struct list_head *head = &mm->ml_entry;
BUG_ON(mm->num_unused != 0);
}
EXPORT_SYMBOL(drm_mm_takedown);
+
+void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
+{
+ struct drm_mm_node *entry;
+ int total_used = 0, total_free = 0, total = 0;
+
+ list_for_each_entry(entry, &mm->ml_entry, ml_entry) {
+ printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n",
+ prefix, entry->start, entry->start + entry->size,
+ entry->size, entry->free ? "free" : "used");
+ total += entry->size;
+ if (entry->free)
+ total_free += entry->size;
+ else
+ total_used += entry->size;
+ }
+ printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total,
+ total_used, total_free);
+}
+EXPORT_SYMBOL(drm_mm_debug_table);
+
+#if defined(CONFIG_DEBUG_FS)
+int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
+{
+ struct drm_mm_node *entry;
+ int total_used = 0, total_free = 0, total = 0;
+
+ list_for_each_entry(entry, &mm->ml_entry, ml_entry) {
+ seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: %s\n", entry->start, entry->start + entry->size, entry->size, entry->free ? "free" : "used");
+ total += entry->size;
+ if (entry->free)
+ total_free += entry->size;
+ else
+ total_used += entry->size;
+ }
+ seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free);
+ return 0;
+}
+EXPORT_SYMBOL(drm_mm_dump_table);
+#endif