summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@wiggum>2005-08-27 09:38:00 +0200
committerJens Axboe <axboe@wiggum>2005-08-27 09:38:00 +0200
commit8fc0abbc65934154363812eb8f7b400a73441905 (patch)
tree31de42c0f5b5ec632f529db57254f273a08d0d59
parentba46ec782eb2db0786346f169cdcaf9cab118ece (diff)
downloadblktrace-8fc0abbc65934154363812eb8f7b400a73441905.tar.gz
blktrace-8fc0abbc65934154363812eb8f7b400a73441905.tar.bz2
Fixed the sorting and parsing of traces with payloads
-rw-r--r--CHANGELOG5
-rw-r--r--Makefile2
-rw-r--r--blkparse.c188
-rw-r--r--blktrace.c4
-rw-r--r--rbtree.c386
-rw-r--r--rbtree.h151
6 files changed, 664 insertions, 72 deletions
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..5bd8d9e
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,5 @@
+20050826:
+ - Committed initial version
+ - Misc fixes
+ - Bug with pdu_len in blkparse.c, rewrote to index in rbtree and
+ ditch usage of qsort()
diff --git a/Makefile b/Makefile
index 6b2f30a..6d00bef 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ TRACE_LIBS = -lpthread
all: $(PROG)
-blkparse: blkparse.o
+blkparse: blkparse.o rbtree.o
blktrace: blktrace.o $(TRACE_LIBS)
clean:
diff --git a/blkparse.c b/blkparse.c
index 64d8c38..a5bd7f1 100644
--- a/blkparse.c
+++ b/blkparse.c
@@ -4,8 +4,10 @@
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
+#include <string.h>
-#include "blktrace.h"
+#include "blktrace.h"
+#include "rbtree.h"
#define NELEMS(pfi) ((pfi)->stat.st_size / sizeof(struct blk_io_trace))
@@ -23,9 +25,18 @@ struct per_file_info {
int dfd;
char *dname;
+ void *trace_buf;
+
unsigned long long start_time;
};
+static struct rb_root rb_root;
+
+struct trace {
+ struct blk_io_trace *bit;
+ struct rb_node rb_node;
+};
+
struct per_file_info per_file_info[MAX_CPUS];
struct per_file_info *current;
@@ -271,12 +282,98 @@ int compar(const void *t1, const void *t2)
return v1 - v2;
}
+inline int trace_rb_insert(struct trace *t)
+{
+ struct rb_node **p = &rb_root.rb_node;
+ struct rb_node *parent = NULL;
+ struct trace *__t;
+
+ while (*p) {
+ parent = *p;
+ __t = rb_entry(parent, struct trace, rb_node);
+
+ if (t->bit->sequence < __t->bit->sequence)
+ p = &(*p)->rb_left;
+ else if (t->bit->sequence > __t->bit->sequence)
+ p = &(*p)->rb_right;
+ else {
+ fprintf(stderr, "sequence alias %u!\n", t->bit->sequence);
+ return 1;
+ }
+ }
+
+ rb_link_node(&t->rb_node, parent, p);
+ rb_insert_color(&t->rb_node, &rb_root);
+ return 0;
+}
+
+int sort_entries(void *traces, unsigned long offset)
+{
+ struct blk_io_trace *bit;
+ struct trace *t;
+ void *start = traces;
+ int nelems = 0;
+
+ memset(&rb_root, 0, sizeof(rb_root));
+
+ do {
+ bit = traces;
+ t = malloc(sizeof(*t));
+ t->bit = bit;
+ memset(&t->rb_node, 0, sizeof(t->rb_node));
+
+ if (trace_rb_insert(t))
+ return -1;
+
+ traces += sizeof(*bit) + bit->pdu_len;
+ nelems++;
+ } while (traces < start + offset);
+
+ return nelems;
+}
+
+void show_entries(void)
+{
+ struct rb_node *n = rb_first(&rb_root);
+ struct blk_io_trace *bit;
+ struct trace *t;
+ int cpu;
+
+ do {
+ if (!n)
+ break;
+
+ t = rb_entry(n, struct trace, rb_node);
+ bit = t->bit;
+
+ cpu = bit->magic;
+ if (cpu >= MAX_CPUS) {
+ fprintf(stderr, "CPU number too large (%d)\n", cpu);
+ return;
+ }
+
+ current = &per_file_info[cpu];
+
+ /*
+ * offset time by first trace event.
+ *
+ * NOTE: This is *cpu* relative, thus you can not
+ * compare times ACROSS cpus.
+ */
+ if (current->start_time == 0)
+ current->start_time = bit->time;
+
+ bit->time -= current->start_time;
+
+ dump_trace(bit);
+ } while ((n = rb_next(n)) != NULL);
+}
+
int main(int argc, char *argv[])
{
- char *p, *dev;
- int i, nfiles, nelems, nb, ret;
+ char *dev;
+ int i, nfiles, nelems, ret;
struct per_file_info *pfi;
- struct blk_io_trace *traces, *tip;
if (argc != 2) {
fprintf(stderr, "Usage %s <dev>\n", argv[0]);
@@ -285,7 +382,6 @@ int main(int argc, char *argv[])
dev = argv[1];
- printf("First pass:\n");
nfiles = nelems = 0;
for (i = 0, pfi = &per_file_info[0]; i < MAX_CPUS; i++, pfi++) {
pfi->cpu = i;
@@ -295,39 +391,6 @@ int main(int argc, char *argv[])
sprintf(pfi->fname, "%s_out.%d", dev, i);
if (stat(pfi->fname, &pfi->stat) < 0)
break;
- if (!S_ISREG(pfi->stat.st_mode)) {
- fprintf(stderr, "Bad file type %s\n", pfi->fname);
- return 1;
- }
-
- nfiles++;
- pfi->nelems = NELEMS(pfi);
- nelems += pfi->nelems;
- printf("\t%2d %10s %15d\n", i, pfi->fname, pfi->nelems);
-
- }
- printf("\t %15d\n", nelems);
-
- if (!i) {
- fprintf(stderr, "No files found\n");
- return 1;
- }
-
- traces = malloc(nelems * sizeof(struct blk_io_trace));
- if (traces == NULL) {
- fprintf(stderr, "Can not allocate %d\n",
- nelems * (int) sizeof(struct blk_io_trace));
- return 1;
- }
-
- printf("Second pass:\n");
- p = (char *)traces;
- for (i = 0, pfi = per_file_info; i < nfiles; i++, pfi++) {
- pfi->fd = open(pfi->fname, O_RDONLY);
- if (pfi->fd < 0) {
- perror(pfi->fname);
- return 1;
- }
pfi->dname = malloc(128);
snprintf(pfi->dname, 127, "%s_dat.%d", dev, i);
@@ -345,47 +408,34 @@ int main(int argc, char *argv[])
return 1;
}
- printf("\tProcessing %s...", pfi->fname); fflush(stdout);
- nb = pfi->stat.st_size;
- ret = read(pfi->fd, p, nb);
- if (ret != nb) {
+ printf("Processing %s\n", pfi->fname);
+
+ pfi->trace_buf = malloc(pfi->stat.st_size);
+
+ pfi->fd = open(pfi->fname, O_RDONLY);
+ if (pfi->fd < 0) {
perror(pfi->fname);
- fprintf(stderr,"\nFATAL: read(%d) -> %d\n", nb, ret);
return 1;
}
- printf("\n"); fflush(stdout);
- p += nb;
- close(pfi->fd);
- }
-
- printf("Sorting..."); fflush(stdout);
- qsort(traces, nelems, sizeof(struct blk_io_trace), compar);
- printf("\n\n");
-
- for (i = 0, tip = traces; i < nelems; i++, tip++) {
- int cpu = tip->magic;
-
- if (cpu >= MAX_CPUS) {
- fprintf(stderr, "CPU number too large (%d)\n", cpu);
+ if (read(pfi->fd, pfi->trace_buf, pfi->stat.st_size) != pfi->stat.st_size) {
+ fprintf(stderr, "error reading\n");
return 1;
}
- current = &per_file_info[cpu];
-
- /*
- * offset time by first trace event.
- *
- * NOTE: This is *cpu* relative, thus you can not
- * compare times ACROSS cpus.
- */
- if (current->start_time == 0)
- current->start_time = tip->time;
+ ret = sort_entries(pfi->trace_buf, pfi->stat.st_size);
+ if (ret == -1)
+ return 1;
- tip->time -= current->start_time;
+ close(pfi->fd);
+ nfiles++;
+ pfi->nelems = ret;
+ nelems += pfi->nelems;
+ printf("\t%2d %10s %15d\n", i, pfi->fname, pfi->nelems);
- dump_trace(tip);
}
+ show_entries();
+
show_stats();
return 0;
}
diff --git a/blktrace.c b/blktrace.c
index c7150f2..2681a8e 100644
--- a/blktrace.c
+++ b/blktrace.c
@@ -166,8 +166,8 @@ void *extract(void *arg)
tip->cpu, ip);
exit(1);
} else if (ret > 0) {
- fprintf(stderr,"Thread %d misread %s %d,%ld\n",
- tip->cpu, ip, ret, sizeof(t));
+ fprintf(stderr,"Thread %d misread %s %d,%d\n",
+ tip->cpu, ip, ret, (int)sizeof(t));
exit(1);
} else {
usleep(10000);
diff --git a/rbtree.c b/rbtree.c
new file mode 100644
index 0000000..37bc021
--- /dev/null
+++ b/rbtree.c
@@ -0,0 +1,386 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+ (C) 2002 David Woodhouse <dwmw2@infradead.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/lib/rbtree.c
+*/
+
+#include "rbtree.h"
+
+static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *right = node->rb_right;
+
+ if ((node->rb_right = right->rb_left))
+ right->rb_left->rb_parent = node;
+ right->rb_left = node;
+
+ if ((right->rb_parent = node->rb_parent))
+ {
+ if (node == node->rb_parent->rb_left)
+ node->rb_parent->rb_left = right;
+ else
+ node->rb_parent->rb_right = right;
+ }
+ else
+ root->rb_node = right;
+ node->rb_parent = right;
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *left = node->rb_left;
+
+ if ((node->rb_left = left->rb_right))
+ left->rb_right->rb_parent = node;
+ left->rb_right = node;
+
+ if ((left->rb_parent = node->rb_parent))
+ {
+ if (node == node->rb_parent->rb_right)
+ node->rb_parent->rb_right = left;
+ else
+ node->rb_parent->rb_left = left;
+ }
+ else
+ root->rb_node = left;
+ node->rb_parent = left;
+}
+
+void rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *parent, *gparent;
+
+ while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
+ {
+ gparent = parent->rb_parent;
+
+ if (parent == gparent->rb_left)
+ {
+ {
+ register struct rb_node *uncle = gparent->rb_right;
+ if (uncle && uncle->rb_color == RB_RED)
+ {
+ uncle->rb_color = RB_BLACK;
+ parent->rb_color = RB_BLACK;
+ gparent->rb_color = RB_RED;
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_right == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ parent->rb_color = RB_BLACK;
+ gparent->rb_color = RB_RED;
+ __rb_rotate_right(gparent, root);
+ } else {
+ {
+ register struct rb_node *uncle = gparent->rb_left;
+ if (uncle && uncle->rb_color == RB_RED)
+ {
+ uncle->rb_color = RB_BLACK;
+ parent->rb_color = RB_BLACK;
+ gparent->rb_color = RB_RED;
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_left == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ parent->rb_color = RB_BLACK;
+ gparent->rb_color = RB_RED;
+ __rb_rotate_left(gparent, root);
+ }
+ }
+
+ root->rb_node->rb_color = RB_BLACK;
+}
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+ struct rb_root *root)
+{
+ struct rb_node *other;
+
+ while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
+ {
+ if (parent->rb_left == node)
+ {
+ other = parent->rb_right;
+ if (other->rb_color == RB_RED)
+ {
+ other->rb_color = RB_BLACK;
+ parent->rb_color = RB_RED;
+ __rb_rotate_left(parent, root);
+ other = parent->rb_right;
+ }
+ if ((!other->rb_left ||
+ other->rb_left->rb_color == RB_BLACK)
+ && (!other->rb_right ||
+ other->rb_right->rb_color == RB_BLACK))
+ {
+ other->rb_color = RB_RED;
+ node = parent;
+ parent = node->rb_parent;
+ }
+ else
+ {
+ if (!other->rb_right ||
+ other->rb_right->rb_color == RB_BLACK)
+ {
+ register struct rb_node *o_left;
+ if ((o_left = other->rb_left))
+ o_left->rb_color = RB_BLACK;
+ other->rb_color = RB_RED;
+ __rb_rotate_right(other, root);
+ other = parent->rb_right;
+ }
+ other->rb_color = parent->rb_color;
+ parent->rb_color = RB_BLACK;
+ if (other->rb_right)
+ other->rb_right->rb_color = RB_BLACK;
+ __rb_rotate_left(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->rb_left;
+ if (other->rb_color == RB_RED)
+ {
+ other->rb_color = RB_BLACK;
+ parent->rb_color = RB_RED;
+ __rb_rotate_right(parent, root);
+ other = parent->rb_left;
+ }
+ if ((!other->rb_left ||
+ other->rb_left->rb_color == RB_BLACK)
+ && (!other->rb_right ||
+ other->rb_right->rb_color == RB_BLACK))
+ {
+ other->rb_color = RB_RED;
+ node = parent;
+ parent = node->rb_parent;
+ }
+ else
+ {
+ if (!other->rb_left ||
+ other->rb_left->rb_color == RB_BLACK)
+ {
+ register struct rb_node *o_right;
+ if ((o_right = other->rb_right))
+ o_right->rb_color = RB_BLACK;
+ other->rb_color = RB_RED;
+ __rb_rotate_left(other, root);
+ other = parent->rb_left;
+ }
+ other->rb_color = parent->rb_color;
+ parent->rb_color = RB_BLACK;
+ if (other->rb_left)
+ other->rb_left->rb_color = RB_BLACK;
+ __rb_rotate_right(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ }
+ if (node)
+ node->rb_color = RB_BLACK;
+}
+
+void rb_erase(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *child, *parent;
+ int color;
+
+ if (!node->rb_left)
+ child = node->rb_right;
+ else if (!node->rb_right)
+ child = node->rb_left;
+ else
+ {
+ struct rb_node *old = node, *left;
+
+ node = node->rb_right;
+ while ((left = node->rb_left) != NULL)
+ node = left;
+ child = node->rb_right;
+ parent = node->rb_parent;
+ color = node->rb_color;
+
+ if (child)
+ child->rb_parent = parent;
+ if (parent)
+ {
+ if (parent->rb_left == node)
+ parent->rb_left = child;
+ else
+ parent->rb_right = child;
+ }
+ else
+ root->rb_node = child;
+
+ if (node->rb_parent == old)
+ parent = node;
+ node->rb_parent = old->rb_parent;
+ node->rb_color = old->rb_color;
+ node->rb_right = old->rb_right;
+ node->rb_left = old->rb_left;
+
+ if (old->rb_parent)
+ {
+ if (old->rb_parent->rb_left == old)
+ old->rb_parent->rb_left = node;
+ else
+ old->rb_parent->rb_right = node;
+ } else
+ root->rb_node = node;
+
+ old->rb_left->rb_parent = node;
+ if (old->rb_right)
+ old->rb_right->rb_parent = node;
+ goto color;
+ }
+
+ parent = node->rb_parent;
+ color = node->rb_color;
+
+ if (child)
+ child->rb_parent = parent;
+ if (parent)
+ {
+ if (parent->rb_left == node)
+ parent->rb_left = child;
+ else
+ parent->rb_right = child;
+ }
+ else
+ root->rb_node = child;
+
+ color:
+ if (color == RB_BLACK)
+ __rb_erase_color(child, parent, root);
+}
+
+/*
+ * This function returns the first node (in sort order) of the tree.
+ */
+struct rb_node *rb_first(struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_left)
+ n = n->rb_left;
+ return n;
+}
+
+struct rb_node *rb_last(struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_right)
+ n = n->rb_right;
+ return n;
+}
+
+struct rb_node *rb_next(struct rb_node *node)
+{
+ /* If we have a right-hand child, go down and then left as far
+ as we can. */
+ if (node->rb_right) {
+ node = node->rb_right;
+ while (node->rb_left)
+ node=node->rb_left;
+ return node;
+ }
+
+ /* No right-hand children. Everything down and left is
+ smaller than us, so any 'next' node must be in the general
+ direction of our parent. Go up the tree; any time the
+ ancestor is a right-hand child of its parent, keep going
+ up. First time it's a left-hand child of its parent, said
+ parent is our 'next' node. */
+ while (node->rb_parent && node == node->rb_parent->rb_right)
+ node = node->rb_parent;
+
+ return node->rb_parent;
+}
+
+struct rb_node *rb_prev(struct rb_node *node)
+{
+ /* If we have a left-hand child, go down and then right as far
+ as we can. */
+ if (node->rb_left) {
+ node = node->rb_left;
+ while (node->rb_right)
+ node=node->rb_right;
+ return node;
+ }
+
+ /* No left-hand children. Go up till we find an ancestor which
+ is a right-hand child of its parent */
+ while (node->rb_parent && node == node->rb_parent->rb_left)
+ node = node->rb_parent;
+
+ return node->rb_parent;
+}
+
+void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root)
+{
+ struct rb_node *parent = victim->rb_parent;
+
+ /* Set the surrounding nodes to point to the replacement */
+ if (parent) {
+ if (victim == parent->rb_left)
+ parent->rb_left = new;
+ else
+ parent->rb_right = new;
+ } else {
+ root->rb_node = new;
+ }
+ if (victim->rb_left)
+ victim->rb_left->rb_parent = new;
+ if (victim->rb_right)
+ victim->rb_right->rb_parent = new;
+
+ /* Copy the pointers/colour from the victim to the replacement */
+ *new = *victim;
+}
diff --git a/rbtree.h b/rbtree.h
new file mode 100644
index 0000000..e722b1e
--- /dev/null
+++ b/rbtree.h
@@ -0,0 +1,151 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ linux/include/linux/rbtree.h
+
+ To use rbtrees you'll have to implement your own insert and search cores.
+ This will avoid us to use callbacks and to drop drammatically performances.
+ I know it's not the cleaner way, but in C (not in C++) to get
+ performances and genericity...
+
+ Some example of insert and search follows here. The search is a plain
+ normal search over an ordered tree. The insert instead must be implemented
+ int two steps: as first thing the code must insert the element in
+ order as a red leaf in the tree, then the support library function
+ rb_insert_color() must be called. Such function will do the
+ not trivial work to rebalance the rbtree if necessary.
+
+-----------------------------------------------------------------------
+static inline struct page * rb_search_page_cache(struct inode * inode,
+ unsigned long offset)
+{
+ struct rb_node * n = inode->i_rb_page_cache.rb_node;
+ struct page * page;
+
+ while (n)
+ {
+ page = rb_entry(n, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ n = n->rb_left;
+ else if (offset > page->offset)
+ n = n->rb_right;
+ else
+ return page;
+ }
+ return NULL;
+}
+
+static inline struct page * __rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
+ struct rb_node * parent = NULL;
+ struct page * page;
+
+ while (*p)
+ {
+ parent = *p;
+ page = rb_entry(parent, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ p = &(*p)->rb_left;
+ else if (offset > page->offset)
+ p = &(*p)->rb_right;
+ else
+ return page;
+ }
+
+ rb_link_node(node, parent, p);
+
+ return NULL;
+}
+
+static inline struct page * rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct page * ret;
+ if ((ret = __rb_insert_page_cache(inode, offset, node)))
+ goto out;
+ rb_insert_color(node, &inode->i_rb_page_cache);
+ out:
+ return ret;
+}
+-----------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_RBTREE_H
+#define _LINUX_RBTREE_H
+
+#include <stdlib.h>
+
+struct rb_node
+{
+ struct rb_node *rb_parent;
+ int rb_color;
+#define RB_RED 0
+#define RB_BLACK 1
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+};
+
+struct rb_root
+{
+ struct rb_node *rb_node;
+};
+
+#undef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define RB_ROOT (struct rb_root) { NULL, }
+#define rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+extern void rb_insert_color(struct rb_node *, struct rb_root *);
+extern void rb_erase(struct rb_node *, struct rb_root *);
+
+/* Find logical next and previous nodes in a tree */
+extern struct rb_node *rb_next(struct rb_node *);
+extern struct rb_node *rb_prev(struct rb_node *);
+extern struct rb_node *rb_first(struct rb_root *);
+extern struct rb_node *rb_last(struct rb_root *);
+
+/* Fast replacement of a single node without remove/rebalance/add/rebalance */
+extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root);
+
+static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
+ struct rb_node ** rb_link)
+{
+ node->rb_parent = parent;
+ node->rb_color = RB_RED;
+ node->rb_left = node->rb_right = NULL;
+
+ *rb_link = node;
+}
+
+#endif /* _LINUX_RBTREE_H */