In addition to the last-in/first-out stack for accessing drm_mm nodes, we occasionally and in the future often want to find a drm_mm_node by an address. To do so efficiently we need to track the nodes in an interval tree - lookups for a particular address will then be O(lg(N)), where N is the number of nodes in the range manager as opposed to O(N). Insertion however gains an extra O(lg(N)) step for all nodes irrespective of whether the interval tree is in use. For future i915 patches, eliminating the linear walk is a significant improvement.
v2: Use generic interval-tree template for u64 and faster insertion.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org --- drivers/gpu/drm/drm_mm.c | 133 +++++++++++++++++++++++++++++++++++++++-------- include/drm/drm_mm.h | 12 +++++ 2 files changed, 122 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45d6a16..5c188c56894b 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,7 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h>
/** * DOC: Overview @@ -103,6 +104,72 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags);
+#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1) + +INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, + u64, __subtree_last, + START, LAST, static inline, drm_mm_interval_tree) + +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_first(&mm->interval_tree, + start, last); +} +EXPORT_SYMBOL(drm_mm_interval_first); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_next(node, start, last); +} +EXPORT_SYMBOL(drm_mm_interval_next); + +static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, + struct drm_mm_node *node) +{ + struct drm_mm *mm = hole_node->mm; + struct rb_node **link, *rb; + struct drm_mm_node *parent; + + node->__subtree_last = LAST(node); + + if (hole_node->allocated) { + rb = &hole_node->rb; + while (rb) { + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last >= node->__subtree_last) + break; + + parent->__subtree_last = node->__subtree_last; + rb = rb_parent(rb); + } + + rb = &hole_node->rb; + link = &hole_node->rb.rb_right; + } else { + rb = NULL; + link = &mm->interval_tree.rb_node; + } + + while (*link) { + rb = *link; + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last < node->__subtree_last) + parent->__subtree_last = node->__subtree_last; + if (node->start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + rb_link_node(&node->rb, rb, link); + rb_insert_augmented(&node->rb, + &mm->interval_tree, + &drm_mm_interval_tree_augment); +} + static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -153,6 +220,8 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
+ drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start + node->size > adj_end);
node->hole_follows = 0; @@ -178,41 +247,52 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { + u64 end = node->start + node->size; struct drm_mm_node *hole; - u64 end; - u64 hole_start; - u64 hole_end; - - BUG_ON(node == NULL); + u64 hole_start, hole_end;
end = node->start + node->size;
/* Find the relevant hole to add our node to */ - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > node->start || hole_end < end) - continue; + hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, + node->start, ~(u64)0); + if (hole) { + if (hole->start < end) + return -ENOSPC; + } else { + hole = list_entry(&mm->head_node.node_list, + typeof(*hole), node_list); + }
- node->mm = mm; - node->allocated = 1; + hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); + if (!hole->hole_follows) + return -ENOSPC;
- INIT_LIST_HEAD(&node->hole_stack); - list_add(&node->node_list, &hole->node_list); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + if (hole_start > node->start || hole_end < end) + return -ENOSPC;
- if (node->start == hole_start) { - hole->hole_follows = 0; - list_del_init(&hole->hole_stack); - } + node->mm = mm; + node->allocated = 1;
- node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole->node_list);
- return 0; + drm_mm_interval_tree_add_node(hole, node); + + if (node->start == hole_start) { + hole->hole_follows = 0; + list_del_init(&hole->hole_stack); + } + + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; }
- return -ENOSPC; + return 0; } EXPORT_SYMBOL(drm_mm_reserve_node);
@@ -302,6 +382,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
+ drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); @@ -390,6 +472,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) } else list_move(&prev_node->hole_stack, &mm->hole_stack);
+ drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0; } @@ -516,11 +599,13 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color; + new->__subtree_last = old->__subtree_last;
old->allocated = 0; new->allocated = 1; @@ -758,6 +843,8 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
+ mm->interval_tree = RB_ROOT; + mm->color_adjust = NULL; } EXPORT_SYMBOL(drm_mm_init); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index fc65118e5077..205ddcf6d55d 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -37,6 +37,7 @@ * Generic range manager structs */ #include <linux/bug.h> +#include <linux/rbtree.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -61,6 +62,7 @@ enum drm_mm_allocator_flags { struct drm_mm_node { struct list_head node_list; struct list_head hole_stack; + struct rb_node rb; unsigned hole_follows : 1; unsigned scanned_block : 1; unsigned scanned_prev_free : 1; @@ -70,6 +72,7 @@ struct drm_mm_node { unsigned long color; u64 start; u64 size; + u64 __subtree_last; struct drm_mm *mm; };
@@ -79,6 +82,9 @@ struct drm_mm { /* head_node.node_list is the list of all memory nodes, ordered * according to the (increasing) start address of the memory node. */ struct drm_mm_node head_node; + /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */ + struct rb_root interval_tree; + unsigned int scan_check_range : 1; unsigned scan_alignment; unsigned long scan_color; @@ -295,6 +301,12 @@ void drm_mm_init(struct drm_mm *mm, void drm_mm_takedown(struct drm_mm *mm); bool drm_mm_clean(struct drm_mm *mm);
+struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last); + void drm_mm_init_scan(struct drm_mm *mm, u64 size, unsigned alignment,
Having added an interval-tree to struct drm_mm, we can replace the auxiliary rb-tree inside the drm_vma_manager with it.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org --- drivers/gpu/drm/drm_vma_manager.c | 43 ++++++++------------------------------- include/drm/drm_vma_manager.h | 2 -- 2 files changed, 9 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index f306c8855978..0aef432679f9 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -86,7 +86,6 @@ void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, unsigned long page_offset, unsigned long size) { rwlock_init(&mgr->vm_lock); - mgr->vm_addr_space_rb = RB_ROOT; drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size); } EXPORT_SYMBOL(drm_vma_offset_manager_init); @@ -145,16 +144,16 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m unsigned long start, unsigned long pages) { - struct drm_vma_offset_node *node, *best; + struct drm_mm_node *node, *best; struct rb_node *iter; unsigned long offset;
- iter = mgr->vm_addr_space_rb.rb_node; + iter = mgr->vm_addr_space_mm.interval_tree.rb_node; best = NULL;
while (likely(iter)) { - node = rb_entry(iter, struct drm_vma_offset_node, vm_rb); - offset = node->vm_node.start; + node = rb_entry(iter, struct drm_mm_node, rb); + offset = node->start; if (start >= offset) { iter = iter->rb_right; best = node; @@ -167,38 +166,17 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m
/* verify that the node spans the requested area */ if (best) { - offset = best->vm_node.start + best->vm_node.size; + offset = best->start + best->size; if (offset < start + pages) best = NULL; }
- return best; -} -EXPORT_SYMBOL(drm_vma_offset_lookup_locked); - -/* internal helper to link @node into the rb-tree */ -static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr, - struct drm_vma_offset_node *node) -{ - struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node; - struct rb_node *parent = NULL; - struct drm_vma_offset_node *iter_node; - - while (likely(*iter)) { - parent = *iter; - iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb); + if (!best) + return NULL;
- if (node->vm_node.start < iter_node->vm_node.start) - iter = &(*iter)->rb_left; - else if (node->vm_node.start > iter_node->vm_node.start) - iter = &(*iter)->rb_right; - else - BUG(); - } - - rb_link_node(&node->vm_rb, parent, iter); - rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb); + return container_of(best, struct drm_vma_offset_node, vm_node); } +EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
/** * drm_vma_offset_add() - Add offset node to manager @@ -240,8 +218,6 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, if (ret) goto out_unlock;
- _drm_vma_offset_add_rb(mgr, node); - out_unlock: write_unlock(&mgr->vm_lock); return ret; @@ -265,7 +241,6 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, write_lock(&mgr->vm_lock);
if (drm_mm_node_allocated(&node->vm_node)) { - rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb); drm_mm_remove_node(&node->vm_node); memset(&node->vm_node, 0, sizeof(node->vm_node)); } diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 06ea8e077ec2..afba6fcac853 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -40,13 +40,11 @@ struct drm_vma_offset_file { struct drm_vma_offset_node { rwlock_t vm_lock; struct drm_mm_node vm_node; - struct rb_node vm_rb; struct rb_root vm_files; };
struct drm_vma_offset_manager { rwlock_t vm_lock; - struct rb_root vm_addr_space_rb; struct drm_mm vm_addr_space_mm; };
Hi Chris
On Wed, Aug 3, 2016 at 5:04 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
Having added an interval-tree to struct drm_mm, we can replace the auxiliary rb-tree inside the drm_vma_manager with it.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org
Should have done that right from start when writing drm_vma_manager..
Reviewed-by: David Herrmann dh.herrmann@gmail.com
Thanks David
drivers/gpu/drm/drm_vma_manager.c | 43 ++++++++------------------------------- include/drm/drm_vma_manager.h | 2 -- 2 files changed, 9 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index f306c8855978..0aef432679f9 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -86,7 +86,6 @@ void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, unsigned long page_offset, unsigned long size) { rwlock_init(&mgr->vm_lock);
mgr->vm_addr_space_rb = RB_ROOT; drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
} EXPORT_SYMBOL(drm_vma_offset_manager_init); @@ -145,16 +144,16 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m unsigned long start, unsigned long pages) {
struct drm_vma_offset_node *node, *best;
struct drm_mm_node *node, *best; struct rb_node *iter; unsigned long offset;
iter = mgr->vm_addr_space_rb.rb_node;
iter = mgr->vm_addr_space_mm.interval_tree.rb_node; best = NULL; while (likely(iter)) {
node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
offset = node->vm_node.start;
node = rb_entry(iter, struct drm_mm_node, rb);
offset = node->start; if (start >= offset) { iter = iter->rb_right; best = node;
@@ -167,38 +166,17 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m
/* verify that the node spans the requested area */ if (best) {
offset = best->vm_node.start + best->vm_node.size;
offset = best->start + best->size; if (offset < start + pages) best = NULL; }
return best;
-} -EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
-/* internal helper to link @node into the rb-tree */ -static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
struct drm_vma_offset_node *node)
-{
struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
struct rb_node *parent = NULL;
struct drm_vma_offset_node *iter_node;
while (likely(*iter)) {
parent = *iter;
iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
if (!best)
return NULL;
if (node->vm_node.start < iter_node->vm_node.start)
iter = &(*iter)->rb_left;
else if (node->vm_node.start > iter_node->vm_node.start)
iter = &(*iter)->rb_right;
else
BUG();
}
rb_link_node(&node->vm_rb, parent, iter);
rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
return container_of(best, struct drm_vma_offset_node, vm_node);
} +EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
/**
- drm_vma_offset_add() - Add offset node to manager
@@ -240,8 +218,6 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, if (ret) goto out_unlock;
_drm_vma_offset_add_rb(mgr, node);
out_unlock: write_unlock(&mgr->vm_lock); return ret; @@ -265,7 +241,6 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, write_lock(&mgr->vm_lock);
if (drm_mm_node_allocated(&node->vm_node)) {
rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb); drm_mm_remove_node(&node->vm_node); memset(&node->vm_node, 0, sizeof(node->vm_node)); }
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 06ea8e077ec2..afba6fcac853 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -40,13 +40,11 @@ struct drm_vma_offset_file { struct drm_vma_offset_node { rwlock_t vm_lock; struct drm_mm_node vm_node;
struct rb_node vm_rb; struct rb_root vm_files;
};
struct drm_vma_offset_manager { rwlock_t vm_lock;
struct rb_root vm_addr_space_rb; struct drm_mm vm_addr_space_mm;
};
-- 2.8.1
As we always add this to the drm_mm->hole_stack as our first operation, we do not need to initialise the list node.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org --- drivers/gpu/drm/drm_mm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 5c188c56894b..3f56d4b0cdae 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -217,7 +217,6 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1;
- INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
drm_mm_interval_tree_add_node(hole_node, node); @@ -276,14 +275,13 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) node->mm = mm; node->allocated = 1;
- INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole->node_list);
drm_mm_interval_tree_add_node(hole, node);
if (node->start == hole_start) { hole->hole_follows = 0; - list_del_init(&hole->hole_stack); + list_del(&hole->hole_stack); }
node->hole_follows = 0; @@ -379,7 +377,6 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1;
- INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
drm_mm_interval_tree_add_node(hole_node, node); @@ -833,7 +830,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
/* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0;
Hi Chris
On Wed, Aug 3, 2016 at 5:04 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
As we always add this to the drm_mm->hole_stack as our first operation, we do not need to initialise the list node.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org
drivers/gpu/drm/drm_mm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
"hole_follows" tells whether it is linked or not. So yeah, indeed, redundant.
Reviewed-by: David Herrmann dh.herrmann@gmail.com
Thanks David
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 5c188c56894b..3f56d4b0cdae 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -217,7 +217,6 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1;
INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); drm_mm_interval_tree_add_node(hole_node, node);
@@ -276,14 +275,13 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) node->mm = mm; node->allocated = 1;
INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole->node_list); drm_mm_interval_tree_add_node(hole, node); if (node->start == hole_start) { hole->hole_follows = 0;
list_del_init(&hole->hole_stack);
list_del(&hole->hole_stack); } node->hole_follows = 0;
@@ -379,7 +377,6 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1;
INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); drm_mm_interval_tree_add_node(hole_node, node);
@@ -833,7 +830,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
/* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list);
INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0;
-- 2.8.1
Hey
On Wed, Aug 3, 2016 at 5:04 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
In addition to the last-in/first-out stack for accessing drm_mm nodes, we occasionally and in the future often want to find a drm_mm_node by an address. To do so efficiently we need to track the nodes in an interval tree - lookups for a particular address will then be O(lg(N)), where N is the number of nodes in the range manager as opposed to O(N). Insertion however gains an extra O(lg(N)) step for all nodes irrespective of whether the interval tree is in use. For future i915 patches, eliminating the linear walk is a significant improvement.
v2: Use generic interval-tree template for u64 and faster insertion.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk Cc: David Herrmann dh.herrmann@gmail.com Cc: dri-devel@lists.freedesktop.org
drivers/gpu/drm/drm_mm.c | 133 +++++++++++++++++++++++++++++++++++++++-------- include/drm/drm_mm.h | 12 +++++ 2 files changed, 122 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45d6a16..5c188c56894b 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,7 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h>
/**
- DOC: Overview
@@ -103,6 +104,72 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags);
+#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1)
So this goes nuts with "size == 0". We do not explicitly prevent that from happening, I think, but might be prevented in the upper layers. Might wanna add WARN_ONs?
Otherwise, looks good to me:
Reviewed-by: David Herrmann dh.herrmann@gmail.com
Thanks David
+INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
u64, __subtree_last,
START, LAST, static inline, drm_mm_interval_tree)
+struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{
return drm_mm_interval_tree_iter_first(&mm->interval_tree,
start, last);
+} +EXPORT_SYMBOL(drm_mm_interval_first);
+struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{
return drm_mm_interval_tree_iter_next(node, start, last);
+} +EXPORT_SYMBOL(drm_mm_interval_next);
+static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
struct drm_mm_node *node)
+{
struct drm_mm *mm = hole_node->mm;
struct rb_node **link, *rb;
struct drm_mm_node *parent;
node->__subtree_last = LAST(node);
if (hole_node->allocated) {
rb = &hole_node->rb;
while (rb) {
parent = rb_entry(rb, struct drm_mm_node, rb);
if (parent->__subtree_last >= node->__subtree_last)
break;
parent->__subtree_last = node->__subtree_last;
rb = rb_parent(rb);
}
rb = &hole_node->rb;
link = &hole_node->rb.rb_right;
} else {
rb = NULL;
link = &mm->interval_tree.rb_node;
}
while (*link) {
rb = *link;
parent = rb_entry(rb, struct drm_mm_node, rb);
if (parent->__subtree_last < node->__subtree_last)
parent->__subtree_last = node->__subtree_last;
if (node->start < parent->start)
link = &parent->rb.rb_left;
else
link = &parent->rb.rb_right;
}
rb_link_node(&node->rb, rb, link);
rb_insert_augmented(&node->rb,
&mm->interval_tree,
&drm_mm_interval_tree_augment);
+}
static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -153,6 +220,8 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
drm_mm_interval_tree_add_node(hole_node, node);
BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0;
@@ -178,41 +247,52 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) {
u64 end = node->start + node->size; struct drm_mm_node *hole;
u64 end;
u64 hole_start;
u64 hole_end;
BUG_ON(node == NULL);
u64 hole_start, hole_end; end = node->start + node->size; /* Find the relevant hole to add our node to */
drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
if (hole_start > node->start || hole_end < end)
continue;
hole = drm_mm_interval_tree_iter_first(&mm->interval_tree,
node->start, ~(u64)0);
if (hole) {
if (hole->start < end)
return -ENOSPC;
} else {
hole = list_entry(&mm->head_node.node_list,
typeof(*hole), node_list);
}
node->mm = mm;
node->allocated = 1;
hole = list_last_entry(&hole->node_list, typeof(*hole), node_list);
if (!hole->hole_follows)
return -ENOSPC;
INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole->node_list);
hole_start = __drm_mm_hole_node_start(hole);
hole_end = __drm_mm_hole_node_end(hole);
if (hole_start > node->start || hole_end < end)
return -ENOSPC;
if (node->start == hole_start) {
hole->hole_follows = 0;
list_del_init(&hole->hole_stack);
}
node->mm = mm;
node->allocated = 1;
node->hole_follows = 0;
if (end != hole_end) {
list_add(&node->hole_stack, &mm->hole_stack);
node->hole_follows = 1;
}
INIT_LIST_HEAD(&node->hole_stack);
list_add(&node->node_list, &hole->node_list);
return 0;
drm_mm_interval_tree_add_node(hole, node);
if (node->start == hole_start) {
hole->hole_follows = 0;
list_del_init(&hole->hole_stack);
}
node->hole_follows = 0;
if (end != hole_end) {
list_add(&node->hole_stack, &mm->hole_stack);
node->hole_follows = 1; }
return -ENOSPC;
return 0;
} EXPORT_SYMBOL(drm_mm_reserve_node);
@@ -302,6 +382,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list);
drm_mm_interval_tree_add_node(hole_node, node);
BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end);
@@ -390,6 +472,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) } else list_move(&prev_node->hole_stack, &mm->hole_stack);
drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0;
} @@ -516,11 +599,13 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack);
rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color;
new->__subtree_last = old->__subtree_last; old->allocated = 0; new->allocated = 1;
@@ -758,6 +843,8 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
mm->interval_tree = RB_ROOT;
mm->color_adjust = NULL;
} EXPORT_SYMBOL(drm_mm_init); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index fc65118e5077..205ddcf6d55d 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -37,6 +37,7 @@
- Generic range manager structs
*/ #include <linux/bug.h> +#include <linux/rbtree.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -61,6 +62,7 @@ enum drm_mm_allocator_flags { struct drm_mm_node { struct list_head node_list; struct list_head hole_stack;
struct rb_node rb; unsigned hole_follows : 1; unsigned scanned_block : 1; unsigned scanned_prev_free : 1;
@@ -70,6 +72,7 @@ struct drm_mm_node { unsigned long color; u64 start; u64 size;
u64 __subtree_last; struct drm_mm *mm;
};
@@ -79,6 +82,9 @@ struct drm_mm { /* head_node.node_list is the list of all memory nodes, ordered * according to the (increasing) start address of the memory node. */ struct drm_mm_node head_node;
/* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */
struct rb_root interval_tree;
unsigned int scan_check_range : 1; unsigned scan_alignment; unsigned long scan_color;
@@ -295,6 +301,12 @@ void drm_mm_init(struct drm_mm *mm, void drm_mm_takedown(struct drm_mm *mm); bool drm_mm_clean(struct drm_mm *mm);
+struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last);
+struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last);
void drm_mm_init_scan(struct drm_mm *mm, u64 size, unsigned alignment, -- 2.8.1
At a higher level, all objects are created with definite size i.e. 0 is illegal. In forthcoming patches, this assumption is dependent upon in the drm_mm range manager, i.e. trying to create a drm_mm node with size 0 will have undefined behaviour. Add a couple of WARNs upon creating the drm_mm node to prevent later bugs.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk --- drivers/gpu/drm/drm_mm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45d6a16..e8c15795386d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -185,6 +185,9 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
BUG_ON(node == NULL);
+ if (WARN_ON(node->size == 0)) + return -EINVAL; + end = node->start + node->size;
/* Find the relevant hole to add our node to */ @@ -239,6 +242,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node;
+ if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node) @@ -340,6 +346,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node;
+ if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags);
On Wed, Aug 3, 2016 at 8:26 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
At a higher level, all objects are created with definite size i.e. 0 is illegal. In forthcoming patches, this assumption is dependent upon in the drm_mm range manager, i.e. trying to create a drm_mm node with size 0 will have undefined behaviour. Add a couple of WARNs upon creating the drm_mm node to prevent later bugs.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk
drivers/gpu/drm/drm_mm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
Reviewed-by: David Herrmann dh.herrmann@gmail.com
Thanks David
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45d6a16..e8c15795386d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -185,6 +185,9 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
BUG_ON(node == NULL);
if (WARN_ON(node->size == 0))
return -EINVAL;
end = node->start + node->size; /* Find the relevant hole to add our node to */
@@ -239,6 +242,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node;
if (WARN_ON(size == 0))
return -EINVAL;
hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node)
@@ -340,6 +346,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node;
if (WARN_ON(size == 0))
return -EINVAL;
hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags);
-- 2.8.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Aug 03, 2016 at 07:26:28PM +0100, Chris Wilson wrote:
At a higher level, all objects are created with definite size i.e. 0 is illegal. In forthcoming patches, this assumption is dependent upon in the drm_mm range manager, i.e. trying to create a drm_mm node with size 0 will have undefined behaviour. Add a couple of WARNs upon creating the drm_mm node to prevent later bugs.
Signed-off-by: Chris Wilson chris@chris-wilson.co.uk
Queued all up for -misc, will push out once -rc1 hits. -Daniel
drivers/gpu/drm/drm_mm.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index cb39f45d6a16..e8c15795386d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -185,6 +185,9 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
BUG_ON(node == NULL);
if (WARN_ON(node->size == 0))
return -EINVAL;
end = node->start + node->size;
/* Find the relevant hole to add our node to */
@@ -239,6 +242,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node;
- if (WARN_ON(size == 0))
return -EINVAL;
- hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node)
@@ -340,6 +346,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node;
- if (WARN_ON(size == 0))
return -EINVAL;
- hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags);
-- 2.8.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
dri-devel@lists.freedesktop.org