summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--ctree.h9
-rw-r--r--disk-io.c5
-rw-r--r--extent-tree.c155
-rw-r--r--pending-extent.c152
-rw-r--r--pending-extent.h51
-rw-r--r--rbtree.c389
-rw-r--r--rbtree.h160
8 files changed, 824 insertions, 99 deletions
diff --git a/Makefile b/Makefile
index 02c7961f..25c94939 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ CC=gcc
CFLAGS = -g -Wall -fno-strict-aliasing -Werror
objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o hash.o file-item.o inode-item.o \
- inode-map.o crc32c.o\
+ inode-map.o crc32c.o rbtree.o pending-extent.o \
#
CHECKFLAGS=-D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
-Wuninitialized -Wshadow -Wundef
diff --git a/ctree.h b/ctree.h
index df4cfda3..b37de525 100644
--- a/ctree.h
+++ b/ctree.h
@@ -21,6 +21,7 @@
#include "list.h"
#include "kerncompat.h"
+#include "pending-extent.h"
struct btrfs_trans_handle;
@@ -280,11 +281,12 @@ struct btrfs_fs_info {
struct btrfs_root *fs_root;
struct btrfs_root *extent_root;
struct btrfs_root *tree_root;
- struct btrfs_key current_insert;
struct btrfs_key last_insert;
struct radix_tree_root cache_radix;
- struct radix_tree_root pinned_radix;
struct radix_tree_root block_group_radix;
+ struct pending_tree pending_tree;
+ struct pending_tree pinned_tree;
+ struct pending_tree del_pending;
struct list_head trans;
struct list_head cache;
u64 last_inode_alloc;
@@ -298,8 +300,7 @@ struct btrfs_fs_info {
/*
* in ram representation of the tree. extent_root is used for all allocations
- * and for the extent tree extent_root root. current_insert is used
- * only for the extent tree.
+ * and for the extent tree extent_root root.
*/
struct btrfs_root {
struct btrfs_buffer *node;
diff --git a/disk-io.c b/disk-io.c
index ca5a1365..4c467c5c 100644
--- a/disk-io.c
+++ b/disk-io.c
@@ -343,10 +343,12 @@ struct btrfs_root *open_ctree_fd(int fp, struct btrfs_super_block *super)
int ret;
INIT_RADIX_TREE(&fs_info->cache_radix, GFP_KERNEL);
- INIT_RADIX_TREE(&fs_info->pinned_radix, GFP_KERNEL);
INIT_RADIX_TREE(&fs_info->block_group_radix, GFP_KERNEL);
INIT_LIST_HEAD(&fs_info->trans);
INIT_LIST_HEAD(&fs_info->cache);
+ pending_tree_init(&fs_info->pending_tree);
+ pending_tree_init(&fs_info->pinned_tree);
+ pending_tree_init(&fs_info->del_pending);
fs_info->cache_size = 0;
fs_info->fp = fp;
fs_info->running_transaction = NULL;
@@ -356,7 +358,6 @@ struct btrfs_root *open_ctree_fd(int fp, struct btrfs_super_block *super)
fs_info->last_inode_alloc = 0;
fs_info->last_inode_alloc_dirid = 0;
fs_info->disk_super = super;
- memset(&fs_info->current_insert, 0, sizeof(fs_info->current_insert));
memset(&fs_info->last_insert, 0, sizeof(fs_info->last_insert));
ret = pread(fp, super, sizeof(struct btrfs_super_block),
diff --git a/extent-tree.c b/extent-tree.c
index 95c4a5a4..85705dca 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -25,9 +25,6 @@
#include "print-tree.h"
#include "transaction.h"
-static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
- *orig_root, u64 num_blocks, u64 search_start, u64
- search_end, struct btrfs_key *ins);
static int finish_current_insert(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root);
static int run_pending(struct btrfs_trans_handle *trans, struct btrfs_root
@@ -40,7 +37,6 @@ static int run_pending(struct btrfs_trans_handle *trans, struct btrfs_root
* other allocations are done. The pending tag is also used in the same
* manner for deletes.
*/
-#define CTREE_EXTENT_PENDING_DEL 0
static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 blocknr)
@@ -50,11 +46,8 @@ static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_key key;
struct btrfs_leaf *l;
struct btrfs_extent_item *item;
- struct btrfs_key ins;
u32 refs;
- find_free_extent(trans, root->fs_info->extent_root, 0, 0, (u64)-1,
- &ins);
btrfs_init_path(&path);
key.objectid = blocknr;
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
@@ -126,11 +119,7 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans,
int pending_ret;
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_block_group_item *bi;
- struct btrfs_key ins;
- ret = find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
- if (ret)
- return ret;
ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&cache->key, path, 0, 1);
BUG_ON(ret);
@@ -220,23 +209,18 @@ static int update_block_group(struct btrfs_trans_handle *trans,
int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
btrfs_root *root)
{
- unsigned long gang[8];
u64 first = 0;
- int ret;
- int i;
-
- while(1) {
- ret = radix_tree_gang_lookup(&root->fs_info->pinned_radix,
- (void *)gang, 0,
- ARRAY_SIZE(gang));
- if (!ret)
- break;
- if (!first)
- first = gang[0];
- for (i = 0; i < ret; i++) {
- radix_tree_delete(&root->fs_info->pinned_radix,
- gang[i]);
- }
+ struct pending_extent *pe;
+ struct pending_extent *next;
+
+ pe = find_first_pending_extent(&root->fs_info->pinned_tree, 0);
+ if (pe)
+ first = pe->start;
+ while(pe) {
+ next = next_pending_extent(pe);
+ remove_pending_extent(&root->fs_info->pinned_tree, pe);
+ free_pending_extent(pe);
+ pe = next;
}
root->fs_info->last_insert.objectid = first;
root->fs_info->last_insert.offset = 0;
@@ -248,19 +232,31 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, struct
{
struct btrfs_key ins;
struct btrfs_extent_item extent_item;
- int i;
int ret;
u64 super_blocks_used, root_blocks_used;
struct btrfs_fs_info *info = extent_root->fs_info;
+ struct pending_extent *pe;
+ struct pending_extent *next;
+ struct pending_tree *pending_tree = &info->pending_tree;
btrfs_set_extent_refs(&extent_item, 1);
btrfs_set_extent_owner(&extent_item, extent_root->root_key.objectid);
ins.offset = 1;
btrfs_set_key_type(&ins, BTRFS_EXTENT_ITEM_KEY);
+ pe = find_first_pending_extent(pending_tree, 0);
+ while(pe) {
+ ins.offset = pe->size;
+ ins.objectid = pe->start;
+
+ remove_pending_extent(pending_tree, pe);
+ next = next_pending_extent(pe);
+ if (!next)
+ next = find_first_pending_extent(pending_tree, 0);
+
+ free_pending_extent(pe);
+ pe = next;
+
- for (i = 0; i < extent_root->fs_info->current_insert.type; i++) {
- ins.objectid = extent_root->fs_info->current_insert.objectid +
- i;
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super,
super_blocks_used + 1);
@@ -274,7 +270,6 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, struct
}
BUG_ON(ret);
}
- extent_root->fs_info->current_insert.offset = 0;
return 0;
}
@@ -290,7 +285,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
struct btrfs_root *extent_root = info->extent_root;
int ret;
struct btrfs_extent_item *ei;
- struct btrfs_key ins;
u32 refs;
BUG_ON(pin && num_blocks != 1);
@@ -298,7 +292,6 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
key.offset = num_blocks;
- find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
btrfs_init_path(&path);
ret = btrfs_search_slot(trans, extent_root, &key, &path, -1, 1);
if (ret) {
@@ -316,12 +309,9 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
u64 super_blocks_used, root_blocks_used;
if (pin) {
int err;
- unsigned long bl = blocknr;
- radix_tree_preload(GFP_KERNEL);
- err = radix_tree_insert(&info->pinned_radix,
- blocknr, (void *)bl);
+ err = insert_pending_extent(&info->pinned_tree,
+ blocknr, 1);
BUG_ON(err);
- radix_tree_preload_end();
}
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super,
@@ -352,25 +342,21 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
btrfs_root *extent_root)
{
int ret;
- struct btrfs_buffer *gang[4];
- int i;
-
- while(1) {
- ret = radix_tree_gang_lookup_tag(
- &extent_root->fs_info->cache_radix,
- (void *)gang, 0,
- ARRAY_SIZE(gang),
- CTREE_EXTENT_PENDING_DEL);
- if (!ret)
- break;
- for (i = 0; i < ret; i++) {
- ret = __free_extent(trans, extent_root,
- gang[i]->blocknr, 1, 1);
- radix_tree_tag_clear(&extent_root->fs_info->cache_radix,
- gang[i]->blocknr,
- CTREE_EXTENT_PENDING_DEL);
- btrfs_block_release(extent_root, gang[i]);
- }
+ struct pending_extent *pe;
+ struct pending_extent *next;
+ struct pending_tree *del_pending = &extent_root->fs_info->del_pending;
+
+ pe = find_first_pending_extent(del_pending, 0);
+ while(pe) {
+ remove_pending_extent(del_pending, pe);
+ ret = __free_extent(trans, extent_root,
+ pe->start, 1, 1);
+ BUG_ON(ret);
+ next = next_pending_extent(pe);
+ if (!next)
+ next = find_first_pending_extent(del_pending, 0);
+ free_pending_extent(pe);
+ pe = next;
}
return 0;
}
@@ -378,9 +364,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
static int run_pending(struct btrfs_trans_handle *trans, struct btrfs_root
*extent_root)
{
- while(radix_tree_tagged(&extent_root->fs_info->cache_radix,
- CTREE_EXTENT_PENDING_DEL))
- del_pending_extents(trans, extent_root);
+ del_pending_extents(trans, extent_root);
return 0;
}
@@ -392,14 +376,13 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
*root, u64 blocknr, u64 num_blocks, int pin)
{
struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct btrfs_buffer *t;
int pending_ret;
int ret;
if (root == extent_root) {
- t = find_tree_block(root, blocknr);
- radix_tree_tag_set(&root->fs_info->cache_radix, blocknr,
- CTREE_EXTENT_PENDING_DEL);
+ ret = insert_pending_extent(&root->fs_info->del_pending,
+ blocknr, num_blocks);
+ BUG_ON(ret);
return 0;
}
ret = __free_extent(trans, root, blocknr, num_blocks, pin);
@@ -425,13 +408,11 @@ static int find_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
u64 hole_size = 0;
int slot = 0;
u64 last_block = 0;
- u64 test_block;
int start_found;
struct btrfs_leaf *l;
struct btrfs_root * root = orig_root->fs_info->extent_root;
int total_needed = num_blocks;
- total_needed += (btrfs_header_level(&root->node->node.header) + 1) * 3;
if (root->fs_info->last_insert.objectid > search_start)
search_start = root->fs_info->last_insert.objectid;
@@ -496,18 +477,16 @@ check_pending:
*/
btrfs_release_path(root, &path);
BUG_ON(ins->objectid < search_start);
- for (test_block = ins->objectid;
- test_block < ins->objectid + total_needed; test_block++) {
- if (radix_tree_lookup(&root->fs_info->pinned_radix,
- test_block)) {
- search_start = test_block + 1;
- goto check_failed;
- }
+ if (find_pending_extent(&root->fs_info->pinned_tree,
+ ins->objectid, total_needed)) {
+ search_start = ins->objectid + total_needed;
+ goto check_failed;
+ }
+ if (find_pending_extent(&root->fs_info->pending_tree,
+ ins->objectid, total_needed)) {
+ search_start = ins->objectid + total_needed;
+ goto check_failed;
}
- BUG_ON(root->fs_info->current_insert.offset);
- root->fs_info->current_insert.offset = total_needed - num_blocks;
- root->fs_info->current_insert.objectid = ins->objectid + num_blocks;
- root->fs_info->current_insert.type = 0;
root->fs_info->last_insert.objectid = ins->objectid;
ins->offset = num_blocks;
return 0;
@@ -537,21 +516,17 @@ static int alloc_extent(struct btrfs_trans_handle *trans, struct btrfs_root
btrfs_set_extent_refs(&extent_item, 1);
btrfs_set_extent_owner(&extent_item, owner);
- if (root == extent_root) {
- BUG_ON(extent_root->fs_info->current_insert.offset == 0);
- BUG_ON(num_blocks != 1);
- BUG_ON(extent_root->fs_info->current_insert.type ==
- extent_root->fs_info->current_insert.offset);
- ins->offset = 1;
- ins->objectid = extent_root->fs_info->current_insert.objectid +
- extent_root->fs_info->current_insert.type++;
- return 0;
- }
ret = find_free_extent(trans, root, num_blocks, search_start,
search_end, ins);
if (ret)
return ret;
+ if (root == extent_root) {
+ ret = insert_pending_extent(&root->fs_info->pending_tree,
+ ins->objectid, ins->offset);
+ BUG_ON(ret);
+ return 0;
+ }
super_blocks_used = btrfs_super_blocks_used(info->disk_super);
btrfs_set_super_blocks_used(info->disk_super, super_blocks_used +
num_blocks);
@@ -803,14 +778,10 @@ int btrfs_insert_block_group(struct btrfs_trans_handle *trans,
struct btrfs_key *key,
struct btrfs_block_group_item *bi)
{
- struct btrfs_key ins;
int ret;
int pending_ret;
root = root->fs_info->extent_root;
- ret = find_free_extent(trans, root, 0, 0, (u64)-1, &ins);
- if (ret)
- return ret;
ret = btrfs_insert_item(trans, root, key, bi, sizeof(*bi));
finish_current_insert(trans, root);
pending_ret = run_pending(trans, root);
diff --git a/pending-extent.c b/pending-extent.c
new file mode 100644
index 00000000..e04b0324
--- /dev/null
+++ b/pending-extent.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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 021110-1307, USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "kerncompat.h"
+#include "pending-extent.h"
+
+void pending_tree_init(struct pending_tree *tree)
+{
+ tree->root.rb_node = NULL;
+}
+
+static struct rb_node *tree_insert(struct rb_root *root, u64 offset,
+ u64 size, struct rb_node *node)
+{
+ struct rb_node ** p = &root->rb_node;
+ struct rb_node * parent = NULL;
+ struct pending_extent *entry;
+
+ while(*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct pending_extent, rb_node);
+
+ if (offset + size <= entry->start)
+ p = &(*p)->rb_left;
+ else if (offset >= entry->start + entry->size)
+ p = &(*p)->rb_right;
+ else
+ return parent;
+ }
+
+ entry = rb_entry(parent, struct pending_extent, rb_node);
+ rb_link_node(node, parent, p);
+ rb_insert_color(node, root);
+ return NULL;
+}
+
+static struct rb_node *__tree_search(struct rb_root *root, u64 offset,
+ u64 size, struct rb_node **prev_ret)
+{
+ struct rb_node * n = root->rb_node;
+ struct rb_node *prev = NULL;
+ struct pending_extent *entry;
+ struct pending_extent *prev_entry = NULL;
+
+ while(n) {
+ entry = rb_entry(n, struct pending_extent, rb_node);
+ prev = n;
+ prev_entry = entry;
+
+ if (offset + size <= entry->start)
+ n = n->rb_left;
+ else if (offset >= entry->start + entry->size)
+ n = n->rb_right;
+ else
+ return n;
+ }
+ if (!prev_ret)
+ return NULL;
+
+ while(prev && offset >= prev_entry->start + prev_entry->size) {
+ prev = rb_next(prev);
+ prev_entry = rb_entry(prev, struct pending_extent, rb_node);
+ }
+ *prev_ret = prev;
+ return NULL;
+}
+
+struct pending_extent *alloc_pending_extent(u64 start, u64 size)
+{
+ struct pending_extent *pe = malloc(sizeof(*pe));
+
+ if (!pe)
+ return pe;
+ pe->start = start;
+ pe->size = size;
+ return pe;
+}
+
+int insert_pending_extent(struct pending_tree *tree, u64 start, u64 size)
+{
+ struct pending_extent *pe = alloc_pending_extent(start, size);
+ struct rb_node *found;
+
+ found = tree_insert(&tree->root, start, size, &pe->rb_node);
+
+ if (found)
+ return -EEXIST;
+ return 0;
+}
+
+struct pending_extent *find_pending_extent(struct pending_tree *tree,
+ u64 start, u64 size)
+{
+ struct rb_node *prev;
+ struct rb_node *ret;
+ struct pending_extent *entry;
+
+ ret = __tree_search(&tree->root, start, size, &prev);
+ if (!ret)
+ return NULL;
+
+ entry = rb_entry(ret, struct pending_extent, rb_node);
+ return entry;
+}
+
+struct pending_extent *find_first_pending_extent(struct pending_tree *tree,
+ u64 start)
+{
+ struct rb_node *prev;
+ struct rb_node *ret;
+ struct pending_extent *entry;
+
+ ret = __tree_search(&tree->root, start, 1, &prev);
+ if (!ret)
+ ret = prev;
+ if (!ret)
+ return NULL;
+ entry = rb_entry(ret, struct pending_extent, rb_node);
+ return entry;
+}
+
+struct pending_extent *next_pending_extent(struct pending_extent *pe)
+{
+ struct rb_node *node = rb_next(&pe->rb_node);
+
+ if (!node)
+ return NULL;
+ return rb_entry(node, struct pending_extent, rb_node);
+}
+
+void remove_pending_extent(struct pending_tree *tree,
+ struct pending_extent *pe)
+{
+ rb_erase(&pe->rb_node, &tree->root);
+}
+
diff --git a/pending-extent.h b/pending-extent.h
new file mode 100644
index 00000000..972f9559
--- /dev/null
+++ b/pending-extent.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * 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 021110-1307, USA.
+ */
+
+#ifndef __PENDING_EXTENT__
+#define __PENDING_EXTENT__
+#include "kerncompat.h"
+#include "rbtree.h"
+
+struct pending_tree {
+ struct rb_root root;
+};
+
+struct pending_extent {
+ struct rb_node rb_node;
+ u64 start;
+ u64 size;
+};
+
+void pending_tree_init(struct pending_tree *tree);
+void remove_pending_extent(struct pending_tree *tree,
+ struct pending_extent *pe);
+struct pending_extent *find_first_pending_extent(struct pending_tree *tree,
+ u64 start);
+struct pending_extent *next_pending_extent(struct pending_extent *pe);
+struct pending_extent *find_pending_extent(struct pending_tree *tree,
+ u64 start, u64 size);
+int insert_pending_extent(struct pending_tree *tree, u64 start, u64 size);
+
+static inline void free_pending_extent(struct pending_extent *pe)
+{
+ free(pe);
+}
+
+struct pending_extent *alloc_pending_extent(u64 start, u64 size);
+
+#endif
diff --git a/rbtree.c b/rbtree.c
new file mode 100644
index 00000000..6ad800fd
--- /dev/null
+++ b/rbtree.c
@@ -0,0 +1,389 @@
+/*
+ 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;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_right = right->rb_left))
+ rb_set_parent(right->rb_left, node);
+ right->rb_left = node;
+
+ rb_set_parent(right, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_left)
+ parent->rb_left = right;
+ else
+ parent->rb_right = right;
+ }
+ else
+ root->rb_node = right;
+ rb_set_parent(node, right);
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *left = node->rb_left;
+ struct rb_node *parent = rb_parent(node);
+
+ if ((node->rb_left = left->rb_right))
+ rb_set_parent(left->rb_right, node);
+ left->rb_right = node;
+
+ rb_set_parent(left, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_right)
+ parent->rb_right = left;
+ else
+ parent->rb_left = left;
+ }
+ else
+ root->rb_node = left;
+ rb_set_parent(node, left);
+}
+
+void rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *parent, *gparent;
+
+ while ((parent = rb_parent(node)) && rb_is_red(parent))
+ {
+ gparent = rb_parent(parent);
+
+ if (parent == gparent->rb_left)
+ {
+ {
+ register struct rb_node *uncle = gparent->rb_right;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_right == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_right(gparent, root);
+ } else {
+ {
+ register struct rb_node *uncle = gparent->rb_left;
+ if (uncle && rb_is_red(uncle))
+ {
+ rb_set_black(uncle);
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_left == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ rb_set_black(parent);
+ rb_set_red(gparent);
+ __rb_rotate_left(gparent, root);
+ }
+ }
+
+ rb_set_black(root->rb_node);
+}
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+ struct rb_root *root)
+{
+ struct rb_node *other;
+
+ while ((!node || rb_is_black(node)) && node != root->rb_node)
+ {
+ if (parent->rb_left == node)
+ {
+ other = parent->rb_right;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_left(parent, root);
+ other = parent->rb_right;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_right || rb_is_black(other->rb_right))
+ {
+ struct rb_node *o_left;
+ if ((o_left = other->rb_left))
+ rb_set_black(o_left);
+ rb_set_red(other);
+ __rb_rotate_right(other, root);
+ other = parent->rb_right;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ if (other->rb_right)
+ rb_set_black(other->rb_right);
+ __rb_rotate_left(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->rb_left;
+ if (rb_is_red(other))
+ {
+ rb_set_black(other);
+ rb_set_red(parent);
+ __rb_rotate_right(parent, root);
+ other = parent->rb_left;
+ }
+ if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+ (!other->rb_right || rb_is_black(other->rb_right)))
+ {
+ rb_set_red(other);
+ node = parent;
+ parent = rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_left || rb_is_black(other->rb_left))
+ {
+ register struct rb_node *o_right;
+ if ((o_right = other->rb_right))
+ rb_set_black(o_right);
+ rb_set_red(other);
+ __rb_rotate_left(other, root);
+ other = parent->rb_left;
+ }
+ rb_set_color(other, rb_color(parent));
+ rb_set_black(parent);
+ if (other->rb_left)
+ rb_set_black(other->rb_left);
+ __rb_rotate_right(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ }
+ if (node)
+ rb_set_black(node);
+}
+
+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 = rb_parent(node);
+ color = rb_color(node);
+
+ if (child)
+ rb_set_parent(child, parent);
+ if (parent == old) {
+ parent->rb_right = child;
+ parent = node;
+ } else
+ parent->rb_left = child;
+
+ node->rb_parent_color = old->rb_parent_color;
+ node->rb_right = old->rb_right;
+ node->rb_left = old->rb_left;
+
+ if (rb_parent(old))
+ {
+ if (rb_parent(old)->rb_left == old)
+ rb_parent(old)->rb_left = node;
+ else
+ rb_parent(old)->rb_right = node;
+ } else
+ root->rb_node = node;
+
+ rb_set_parent(old->rb_left, node);
+ if (old->rb_right)
+ rb_set_parent(old->rb_right, node);
+ goto color;
+ }
+
+ parent = rb_parent(node);
+ color = rb_color(node);
+
+ if (child)
+ rb_set_parent(child, 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)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* 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 ((parent = rb_parent(node)) && node == parent->rb_right)
+ node = parent;
+
+ return parent;
+}
+
+struct rb_node *rb_prev(struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (rb_parent(node) == node)
+ return NULL;
+
+ /* 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 ((parent = rb_parent(node)) && node == parent->rb_left)
+ node = parent;
+
+ return parent;
+}
+
+void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root)
+{
+ struct rb_node *parent = rb_parent(victim);
+
+ /* 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)
+ rb_set_parent(victim->rb_left, new);
+ if (victim->rb_right)
+ rb_set_parent(victim->rb_right, 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 00000000..bed054d4
--- /dev/null
+++ b/rbtree.h
@@ -0,0 +1,160 @@
+/*
+ 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 "kerncompat.h"
+struct rb_node
+{
+ unsigned long rb_parent_color;
+#define RB_RED 0
+#define RB_BLACK 1
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+ /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+struct rb_root
+{
+ struct rb_node *rb_node;
+ void (*rotate_notify)(struct rb_node *old_parent, struct rb_node *node);
+
+};
+
+
+#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
+#define rb_color(r) ((r)->rb_parent_color & 1)
+#define rb_is_red(r) (!rb_color(r))
+#define rb_is_black(r) rb_color(r)
+#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
+#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
+
+static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
+}
+static inline void rb_set_color(struct rb_node *rb, int color)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
+}
+
+#define RB_ROOT (struct rb_root) { NULL, }
+#define rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
+#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
+#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
+
+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_color = (unsigned long )parent;
+ node->rb_left = node->rb_right = NULL;
+
+ *rb_link = node;
+}
+
+#endif /* _LINUX_RBTREE_H */