41,10 → 41,11 |
* Thomas Hellström <thomas-at-tungstengraphics-dot-com> |
*/ |
|
#include "drmP.h" |
#include "drm_mm.h" |
#include <drm/drmP.h> |
#include <drm/drm_mm.h> |
#include <linux/slab.h> |
#include <linux/seq_file.h> |
#include <linux/export.h> |
|
#define MM_UNUSED_TARGET 4 |
|
117,39 → 118,46 |
|
static void drm_mm_insert_helper(struct drm_mm_node *hole_node, |
struct drm_mm_node *node, |
unsigned long size, unsigned alignment) |
unsigned long size, unsigned alignment, |
unsigned long color) |
{ |
struct drm_mm *mm = hole_node->mm; |
unsigned long tmp = 0, wasted = 0; |
unsigned long hole_start = drm_mm_hole_node_start(hole_node); |
unsigned long hole_end = drm_mm_hole_node_end(hole_node); |
unsigned long adj_start = hole_start; |
unsigned long adj_end = hole_end; |
|
BUG_ON(!hole_node->hole_follows || node->allocated); |
|
if (alignment) |
tmp = hole_start % alignment; |
if (mm->color_adjust) |
mm->color_adjust(hole_node, color, &adj_start, &adj_end); |
|
if (!tmp) { |
if (alignment) { |
unsigned tmp = adj_start % alignment; |
if (tmp) |
adj_start += alignment - tmp; |
} |
|
if (adj_start == hole_start) { |
hole_node->hole_follows = 0; |
list_del_init(&hole_node->hole_stack); |
} else |
wasted = alignment - tmp; |
list_del(&hole_node->hole_stack); |
} |
|
node->start = hole_start + wasted; |
node->start = adj_start; |
node->size = size; |
node->mm = mm; |
node->color = color; |
node->allocated = 1; |
|
INIT_LIST_HEAD(&node->hole_stack); |
list_add(&node->node_list, &hole_node->node_list); |
|
BUG_ON(node->start + node->size > hole_end); |
BUG_ON(node->start + node->size > adj_end); |
|
node->hole_follows = 0; |
if (node->start + node->size < hole_end) { |
list_add(&node->hole_stack, &mm->hole_stack); |
node->hole_follows = 1; |
} else { |
node->hole_follows = 0; |
} |
} |
|
156,6 → 164,7 |
struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, |
unsigned long size, |
unsigned alignment, |
unsigned long color, |
int atomic) |
{ |
struct drm_mm_node *node; |
164,7 → 173,7 |
if (unlikely(node == NULL)) |
return NULL; |
|
drm_mm_insert_helper(hole_node, node, size, alignment); |
drm_mm_insert_helper(hole_node, node, size, alignment, color); |
|
return node; |
} |
180,11 → 189,11 |
{ |
struct drm_mm_node *hole_node; |
|
hole_node = drm_mm_search_free(mm, size, alignment, 0); |
hole_node = drm_mm_search_free(mm, size, alignment, false); |
if (!hole_node) |
return -ENOSPC; |
|
drm_mm_insert_helper(hole_node, node, size, alignment); |
drm_mm_insert_helper(hole_node, node, size, alignment, 0); |
|
return 0; |
} |
193,44 → 202,50 |
static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, |
struct drm_mm_node *node, |
unsigned long size, unsigned alignment, |
unsigned long color, |
unsigned long start, unsigned long end) |
{ |
struct drm_mm *mm = hole_node->mm; |
unsigned long tmp = 0, wasted = 0; |
unsigned long hole_start = drm_mm_hole_node_start(hole_node); |
unsigned long hole_end = drm_mm_hole_node_end(hole_node); |
unsigned long adj_start = hole_start; |
unsigned long adj_end = hole_end; |
|
BUG_ON(!hole_node->hole_follows || node->allocated); |
|
if (hole_start < start) |
wasted += start - hole_start; |
if (alignment) |
tmp = (hole_start + wasted) % alignment; |
if (mm->color_adjust) |
mm->color_adjust(hole_node, color, &adj_start, &adj_end); |
|
if (adj_start < start) |
adj_start = start; |
|
if (alignment) { |
unsigned tmp = adj_start % alignment; |
if (tmp) |
wasted += alignment - tmp; |
adj_start += alignment - tmp; |
} |
|
if (!wasted) { |
if (adj_start == hole_start) { |
hole_node->hole_follows = 0; |
list_del_init(&hole_node->hole_stack); |
list_del(&hole_node->hole_stack); |
} |
|
node->start = hole_start + wasted; |
node->start = adj_start; |
node->size = size; |
node->mm = mm; |
node->color = color; |
node->allocated = 1; |
|
INIT_LIST_HEAD(&node->hole_stack); |
list_add(&node->node_list, &hole_node->node_list); |
|
BUG_ON(node->start + node->size > hole_end); |
BUG_ON(node->start + node->size > adj_end); |
BUG_ON(node->start + node->size > end); |
|
node->hole_follows = 0; |
if (node->start + node->size < hole_end) { |
list_add(&node->hole_stack, &mm->hole_stack); |
node->hole_follows = 1; |
} else { |
node->hole_follows = 0; |
} |
} |
|
237,6 → 252,7 |
struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node, |
unsigned long size, |
unsigned alignment, |
unsigned long color, |
unsigned long start, |
unsigned long end, |
int atomic) |
247,7 → 263,7 |
if (unlikely(node == NULL)) |
return NULL; |
|
drm_mm_insert_helper_range(hole_node, node, size, alignment, |
drm_mm_insert_helper_range(hole_node, node, size, alignment, color, |
start, end); |
|
return node; |
266,11 → 282,11 |
struct drm_mm_node *hole_node; |
|
hole_node = drm_mm_search_free_in_range(mm, size, alignment, |
start, end, 0); |
start, end, false); |
if (!hole_node) |
return -ENOSPC; |
|
drm_mm_insert_helper_range(hole_node, node, size, alignment, |
drm_mm_insert_helper_range(hole_node, node, size, alignment, 0, |
start, end); |
|
return 0; |
335,8 → 351,6 |
static int check_free_hole(unsigned long start, unsigned long end, |
unsigned long size, unsigned alignment) |
{ |
unsigned wasted = 0; |
|
if (end - start < size) |
return 0; |
|
343,19 → 357,17 |
if (alignment) { |
unsigned tmp = start % alignment; |
if (tmp) |
wasted = alignment - tmp; |
start += alignment - tmp; |
} |
|
if (end >= start + size + wasted) { |
return 1; |
return end >= start + size; |
} |
|
return 0; |
} |
|
struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, |
struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, |
unsigned long size, |
unsigned alignment, int best_match) |
unsigned alignment, |
unsigned long color, |
bool best_match) |
{ |
struct drm_mm_node *entry; |
struct drm_mm_node *best; |
367,10 → 379,17 |
best_size = ~0UL; |
|
list_for_each_entry(entry, &mm->hole_stack, hole_stack) { |
unsigned long adj_start = drm_mm_hole_node_start(entry); |
unsigned long adj_end = drm_mm_hole_node_end(entry); |
|
if (mm->color_adjust) { |
mm->color_adjust(entry, color, &adj_start, &adj_end); |
if (adj_end <= adj_start) |
continue; |
} |
|
BUG_ON(!entry->hole_follows); |
if (!check_free_hole(drm_mm_hole_node_start(entry), |
drm_mm_hole_node_end(entry), |
size, alignment)) |
if (!check_free_hole(adj_start, adj_end, size, alignment)) |
continue; |
|
if (!best_match) |
384,14 → 403,15 |
|
return best; |
} |
EXPORT_SYMBOL(drm_mm_search_free); |
EXPORT_SYMBOL(drm_mm_search_free_generic); |
|
struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm, |
struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, |
unsigned long size, |
unsigned alignment, |
unsigned long color, |
unsigned long start, |
unsigned long end, |
int best_match) |
bool best_match) |
{ |
struct drm_mm_node *entry; |
struct drm_mm_node *best; |
409,6 → 429,13 |
end : drm_mm_hole_node_end(entry); |
|
BUG_ON(!entry->hole_follows); |
|
if (mm->color_adjust) { |
mm->color_adjust(entry, color, &adj_start, &adj_end); |
if (adj_end <= adj_start) |
continue; |
} |
|
if (!check_free_hole(adj_start, adj_end, size, alignment)) |
continue; |
|
423,7 → 450,7 |
|
return best; |
} |
EXPORT_SYMBOL(drm_mm_search_free_in_range); |
EXPORT_SYMBOL(drm_mm_search_free_in_range_generic); |
|
/** |
* Moves an allocation. To be used with embedded struct drm_mm_node. |
436,6 → 463,7 |
new->mm = old->mm; |
new->start = old->start; |
new->size = old->size; |
new->color = old->color; |
|
old->allocated = 0; |
new->allocated = 1; |
451,9 → 479,12 |
* Warning: As long as the scan list is non-empty, no other operations than |
* adding/removing nodes to/from the scan list are allowed. |
*/ |
void drm_mm_init_scan(struct drm_mm *mm, unsigned long size, |
unsigned alignment) |
void drm_mm_init_scan(struct drm_mm *mm, |
unsigned long size, |
unsigned alignment, |
unsigned long color) |
{ |
mm->scan_color = color; |
mm->scan_alignment = alignment; |
mm->scan_size = size; |
mm->scanned_blocks = 0; |
473,11 → 504,14 |
* Warning: As long as the scan list is non-empty, no other operations than |
* adding/removing nodes to/from the scan list are allowed. |
*/ |
void drm_mm_init_scan_with_range(struct drm_mm *mm, unsigned long size, |
void drm_mm_init_scan_with_range(struct drm_mm *mm, |
unsigned long size, |
unsigned alignment, |
unsigned long color, |
unsigned long start, |
unsigned long end) |
{ |
mm->scan_color = color; |
mm->scan_alignment = alignment; |
mm->scan_size = size; |
mm->scanned_blocks = 0; |
521,14 → 555,18 |
|
hole_start = drm_mm_hole_node_start(prev_node); |
hole_end = drm_mm_hole_node_end(prev_node); |
if (mm->scan_check_range) { |
adj_start = hole_start < mm->scan_start ? |
mm->scan_start : hole_start; |
adj_end = hole_end > mm->scan_end ? |
mm->scan_end : hole_end; |
} else { |
|
adj_start = hole_start; |
adj_end = hole_end; |
|
if (mm->color_adjust) |
mm->color_adjust(prev_node, mm->scan_color, &adj_start, &adj_end); |
|
if (mm->scan_check_range) { |
if (adj_start < mm->scan_start) |
adj_start = mm->scan_start; |
if (adj_end > mm->scan_end) |
adj_end = mm->scan_end; |
} |
|
if (check_free_hole(adj_start , adj_end, |
615,6 → 653,8 |
mm->head_node.size = start - mm->head_node.start; |
list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); |
|
mm->color_adjust = NULL; |
|
return 0; |
} |
EXPORT_SYMBOL(drm_mm_init); |