-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 332406 b: refs/heads/master c: fff3fd8 h: refs/heads/master v: v3
- Loading branch information
Michel Lespinasse
authored and
Linus Torvalds
committed
Oct 9, 2012
1 parent
cd9aec2
commit f80bd4f
Showing
8 changed files
with
418 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: 3908836aa77e3621aaf2101f2920e01d7c8460d6 | ||
refs/heads/master: fff3fd8a1210a165252cd7cd01206da7a90d3a06 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#ifndef _LINUX_INTERVAL_TREE_H | ||
#define _LINUX_INTERVAL_TREE_H | ||
|
||
#include <linux/rbtree.h> | ||
|
||
struct interval_tree_node { | ||
struct rb_node rb; | ||
unsigned long start; /* Start of interval */ | ||
unsigned long last; /* Last location _in_ interval */ | ||
unsigned long __subtree_last; | ||
}; | ||
|
||
extern void | ||
interval_tree_insert(struct interval_tree_node *node, struct rb_root *root); | ||
|
||
extern void | ||
interval_tree_remove(struct interval_tree_node *node, struct rb_root *root); | ||
|
||
extern struct interval_tree_node * | ||
interval_tree_iter_first(struct rb_root *root, | ||
unsigned long start, unsigned long last); | ||
|
||
extern struct interval_tree_node * | ||
interval_tree_iter_next(struct interval_tree_node *node, | ||
unsigned long start, unsigned long last); | ||
|
||
#endif /* _LINUX_INTERVAL_TREE_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
#include <linux/init.h> | ||
#include <linux/interval_tree.h> | ||
|
||
/* Callbacks for augmented rbtree insert and remove */ | ||
|
||
static inline unsigned long | ||
compute_subtree_last(struct interval_tree_node *node) | ||
{ | ||
unsigned long max = node->last, subtree_last; | ||
if (node->rb.rb_left) { | ||
subtree_last = rb_entry(node->rb.rb_left, | ||
struct interval_tree_node, rb)->__subtree_last; | ||
if (max < subtree_last) | ||
max = subtree_last; | ||
} | ||
if (node->rb.rb_right) { | ||
subtree_last = rb_entry(node->rb.rb_right, | ||
struct interval_tree_node, rb)->__subtree_last; | ||
if (max < subtree_last) | ||
max = subtree_last; | ||
} | ||
return max; | ||
} | ||
|
||
RB_DECLARE_CALLBACKS(static, augment_callbacks, struct interval_tree_node, rb, | ||
unsigned long, __subtree_last, compute_subtree_last) | ||
|
||
/* Insert / remove interval nodes from the tree */ | ||
|
||
void interval_tree_insert(struct interval_tree_node *node, | ||
struct rb_root *root) | ||
{ | ||
struct rb_node **link = &root->rb_node, *rb_parent = NULL; | ||
unsigned long start = node->start, last = node->last; | ||
struct interval_tree_node *parent; | ||
|
||
while (*link) { | ||
rb_parent = *link; | ||
parent = rb_entry(rb_parent, struct interval_tree_node, rb); | ||
if (parent->__subtree_last < last) | ||
parent->__subtree_last = last; | ||
if (start < parent->start) | ||
link = &parent->rb.rb_left; | ||
else | ||
link = &parent->rb.rb_right; | ||
} | ||
|
||
node->__subtree_last = last; | ||
rb_link_node(&node->rb, rb_parent, link); | ||
rb_insert_augmented(&node->rb, root, &augment_callbacks); | ||
} | ||
|
||
void interval_tree_remove(struct interval_tree_node *node, | ||
struct rb_root *root) | ||
{ | ||
rb_erase_augmented(&node->rb, root, &augment_callbacks); | ||
} | ||
|
||
/* | ||
* Iterate over intervals intersecting [start;last] | ||
* | ||
* Note that a node's interval intersects [start;last] iff: | ||
* Cond1: node->start <= last | ||
* and | ||
* Cond2: start <= node->last | ||
*/ | ||
|
||
static struct interval_tree_node * | ||
subtree_search(struct interval_tree_node *node, | ||
unsigned long start, unsigned long last) | ||
{ | ||
while (true) { | ||
/* | ||
* Loop invariant: start <= node->__subtree_last | ||
* (Cond2 is satisfied by one of the subtree nodes) | ||
*/ | ||
if (node->rb.rb_left) { | ||
struct interval_tree_node *left = | ||
rb_entry(node->rb.rb_left, | ||
struct interval_tree_node, rb); | ||
if (start <= left->__subtree_last) { | ||
/* | ||
* Some nodes in left subtree satisfy Cond2. | ||
* Iterate to find the leftmost such node N. | ||
* If it also satisfies Cond1, that's the match | ||
* we are looking for. Otherwise, there is no | ||
* matching interval as nodes to the right of N | ||
* can't satisfy Cond1 either. | ||
*/ | ||
node = left; | ||
continue; | ||
} | ||
} | ||
if (node->start <= last) { /* Cond1 */ | ||
if (start <= node->last) /* Cond2 */ | ||
return node; /* node is leftmost match */ | ||
if (node->rb.rb_right) { | ||
node = rb_entry(node->rb.rb_right, | ||
struct interval_tree_node, rb); | ||
if (start <= node->__subtree_last) | ||
continue; | ||
} | ||
} | ||
return NULL; /* No match */ | ||
} | ||
} | ||
|
||
struct interval_tree_node * | ||
interval_tree_iter_first(struct rb_root *root, | ||
unsigned long start, unsigned long last) | ||
{ | ||
struct interval_tree_node *node; | ||
|
||
if (!root->rb_node) | ||
return NULL; | ||
node = rb_entry(root->rb_node, struct interval_tree_node, rb); | ||
if (node->__subtree_last < start) | ||
return NULL; | ||
return subtree_search(node, start, last); | ||
} | ||
|
||
struct interval_tree_node * | ||
interval_tree_iter_next(struct interval_tree_node *node, | ||
unsigned long start, unsigned long last) | ||
{ | ||
struct rb_node *rb = node->rb.rb_right, *prev; | ||
|
||
while (true) { | ||
/* | ||
* Loop invariants: | ||
* Cond1: node->start <= last | ||
* rb == node->rb.rb_right | ||
* | ||
* First, search right subtree if suitable | ||
*/ | ||
if (rb) { | ||
struct interval_tree_node *right = | ||
rb_entry(rb, struct interval_tree_node, rb); | ||
if (start <= right->__subtree_last) | ||
return subtree_search(right, start, last); | ||
} | ||
|
||
/* Move up the tree until we come from a node's left child */ | ||
do { | ||
rb = rb_parent(&node->rb); | ||
if (!rb) | ||
return NULL; | ||
prev = &node->rb; | ||
node = rb_entry(rb, struct interval_tree_node, rb); | ||
rb = node->rb.rb_right; | ||
} while (prev == rb); | ||
|
||
/* Check if the node intersects [start;last] */ | ||
if (last < node->start) /* !Cond1 */ | ||
return NULL; | ||
else if (start <= node->last) /* Cond2 */ | ||
return node; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#include <linux/module.h> | ||
#include <linux/interval_tree.h> | ||
#include <linux/random.h> | ||
#include <asm/timex.h> | ||
|
||
#define NODES 100 | ||
#define PERF_LOOPS 100000 | ||
#define SEARCHES 100 | ||
#define SEARCH_LOOPS 10000 | ||
|
||
static struct rb_root root = RB_ROOT; | ||
static struct interval_tree_node nodes[NODES]; | ||
static u32 queries[SEARCHES]; | ||
|
||
static struct rnd_state rnd; | ||
|
||
static inline unsigned long | ||
search(unsigned long query, struct rb_root *root) | ||
{ | ||
struct interval_tree_node *node; | ||
unsigned long results = 0; | ||
|
||
for (node = interval_tree_iter_first(root, query, query); node; | ||
node = interval_tree_iter_next(node, query, query)) | ||
results++; | ||
return results; | ||
} | ||
|
||
static void init(void) | ||
{ | ||
int i; | ||
for (i = 0; i < NODES; i++) { | ||
u32 a = prandom32(&rnd), b = prandom32(&rnd); | ||
if (a <= b) { | ||
nodes[i].start = a; | ||
nodes[i].last = b; | ||
} else { | ||
nodes[i].start = b; | ||
nodes[i].last = a; | ||
} | ||
} | ||
for (i = 0; i < SEARCHES; i++) | ||
queries[i] = prandom32(&rnd); | ||
} | ||
|
||
static int interval_tree_test_init(void) | ||
{ | ||
int i, j; | ||
unsigned long results; | ||
cycles_t time1, time2, time; | ||
|
||
printk(KERN_ALERT "interval tree insert/remove"); | ||
|
||
prandom32_seed(&rnd, 3141592653589793238ULL); | ||
init(); | ||
|
||
time1 = get_cycles(); | ||
|
||
for (i = 0; i < PERF_LOOPS; i++) { | ||
for (j = 0; j < NODES; j++) | ||
interval_tree_insert(nodes + j, &root); | ||
for (j = 0; j < NODES; j++) | ||
interval_tree_remove(nodes + j, &root); | ||
} | ||
|
||
time2 = get_cycles(); | ||
time = time2 - time1; | ||
|
||
time = div_u64(time, PERF_LOOPS); | ||
printk(" -> %llu cycles\n", (unsigned long long)time); | ||
|
||
printk(KERN_ALERT "interval tree search"); | ||
|
||
for (j = 0; j < NODES; j++) | ||
interval_tree_insert(nodes + j, &root); | ||
|
||
time1 = get_cycles(); | ||
|
||
results = 0; | ||
for (i = 0; i < SEARCH_LOOPS; i++) | ||
for (j = 0; j < SEARCHES; j++) | ||
results += search(queries[j], &root); | ||
|
||
time2 = get_cycles(); | ||
time = time2 - time1; | ||
|
||
time = div_u64(time, SEARCH_LOOPS); | ||
results = div_u64(results, SEARCH_LOOPS); | ||
printk(" -> %llu cycles (%lu results)\n", | ||
(unsigned long long)time, results); | ||
|
||
return -EAGAIN; /* Fail will directly unload the module */ | ||
} | ||
|
||
static void interval_tree_test_exit(void) | ||
{ | ||
printk(KERN_ALERT "test exit\n"); | ||
} | ||
|
||
module_init(interval_tree_test_init) | ||
module_exit(interval_tree_test_exit) | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Michel Lespinasse"); | ||
MODULE_DESCRIPTION("Interval Tree test"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.