Btrfs: add a radix back bit tree
authorChris Mason <chris.mason@oracle.com>
Mon, 26 Mar 2007 14:15:30 +0000 (10:15 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Mon, 26 Mar 2007 14:15:30 +0000 (10:15 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/Makefile
fs/btrfs/bit-radix.c [new file with mode: 0644]
fs/btrfs/bit-radix.h [new file with mode: 0644]
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c

index be7d74cdca049cff35f8a5e38a3bac1617887e23..5346f706b2c5d78e3df1319b86c629c98d38712f 100644 (file)
@@ -3,7 +3,8 @@ ifneq ($(KERNELRELEASE),)
 
 obj-m  := btrfs.o
 btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
-          hash.o file-item.o inode-item.o inode-map.o disk-io.o transaction.o
+          hash.o file-item.o inode-item.o inode-map.o disk-io.o \
+          transaction.o bit-radix.o
 
 #btrfs-y := 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 \
diff --git a/fs/btrfs/bit-radix.c b/fs/btrfs/bit-radix.c
new file mode 100644 (file)
index 0000000..43807cc
--- /dev/null
@@ -0,0 +1,107 @@
+#include <linux/module.h>
+#include "bit-radix.h"
+
+#define BIT_ARRAY_BYTES 256
+#define BIT_RADIX_BITS_PER_ARRAY ((BIT_ARRAY_BYTES - sizeof(unsigned long)) * 8)
+
+int set_radix_bit(struct radix_tree_root *radix, unsigned long bit)
+{
+       unsigned long *bits;
+       unsigned long slot;
+       int bit_slot;
+       int ret;
+
+       slot = bit / BIT_RADIX_BITS_PER_ARRAY;
+       bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY;
+
+       bits = radix_tree_lookup(radix, slot);
+       if (!bits) {
+               bits = kmalloc(BIT_ARRAY_BYTES, GFP_NOIO);
+               if (!bits)
+                       return -ENOMEM;
+               memset(bits + 1, 0, BIT_ARRAY_BYTES - sizeof(unsigned long));
+               bits[0] = slot;
+               ret = radix_tree_insert(radix, slot, bits);
+               if (ret)
+                       return ret;
+       }
+       set_bit(bit_slot, bits + 1);
+       return 0;
+}
+
+int test_radix_bit(struct radix_tree_root *radix, unsigned long bit)
+{
+       unsigned long *bits;
+       unsigned long slot;
+       int bit_slot;
+
+       slot = bit / BIT_RADIX_BITS_PER_ARRAY;
+       bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY;
+
+       bits = radix_tree_lookup(radix, slot);
+       if (!bits)
+               return 0;
+       return test_bit(bit_slot, bits + 1);
+}
+
+int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit)
+{
+       unsigned long *bits;
+       unsigned long slot;
+       int bit_slot;
+       int i;
+       int empty = 1;
+
+       slot = bit / BIT_RADIX_BITS_PER_ARRAY;
+       bit_slot = bit % BIT_RADIX_BITS_PER_ARRAY;
+
+       bits = radix_tree_lookup(radix, slot);
+       if (!bits)
+               return 0;
+       clear_bit(bit_slot, bits + 1);
+
+       for (i = 1; i < BIT_ARRAY_BYTES / sizeof(unsigned long); i++) {
+               if (bits[i]) {
+                       empty = 0;
+                       break;
+               }
+       }
+
+       if (empty) {
+               bits = radix_tree_delete(radix, slot);
+               BUG_ON(!bits);
+       }
+       return 0;
+}
+
+int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits,
+                        int nr)
+{
+       unsigned long *bits;
+       unsigned long *gang[4];
+       int found;
+       int ret;
+       int i;
+       int total_found = 0;
+
+       ret = radix_tree_gang_lookup(radix, (void *)&gang, 0, ARRAY_SIZE(gang));
+       for (i = 0; i < ret && nr > 0; i++) {
+               found = 0;
+               bits = gang[i];
+               while(nr > 0) {
+                       found = find_next_bit(bits + 1,
+                                             BIT_RADIX_BITS_PER_ARRAY,
+                                             found);
+                       if (found < BIT_RADIX_BITS_PER_ARRAY) {
+                               *retbits = bits[0] *
+                                       BIT_RADIX_BITS_PER_ARRAY + found;
+                               retbits++;
+                               nr--;
+                               total_found++;
+                               found++;
+                       } else
+                               break;
+               }
+       }
+       return total_found;
+}
diff --git a/fs/btrfs/bit-radix.h b/fs/btrfs/bit-radix.h
new file mode 100644 (file)
index 0000000..56aad4c
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __BIT_RADIX__
+#define __BIT_RADIX__
+#include <linux/radix-tree.h>
+
+int set_radix_bit(struct radix_tree_root *radix, unsigned long bit);
+int test_radix_bit(struct radix_tree_root *radix, unsigned long bit);
+int clear_radix_bit(struct radix_tree_root *radix, unsigned long bit);
+int find_first_radix_bit(struct radix_tree_root *radix, unsigned long *retbits,
+                        int nr);
+
+static inline void init_bit_radix(struct radix_tree_root *radix)
+{
+       INIT_RADIX_TREE(radix, GFP_NOFS);
+}
+#endif
index 68cafae6a85045cddcf7958a4999d2174e2686dd..0aa1052d9f677e8f38014ebf6a5ba61d403a073e 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef __BTRFS__
 #define __BTRFS__
 
-#include <linux/radix-tree.h>
 #include <linux/fs.h>
+#include "bit-radix.h"
 
 struct btrfs_trans_handle;
 struct btrfs_transaction;
@@ -222,6 +222,7 @@ struct btrfs_fs_info {
        struct btrfs_root *inode_root;
        struct btrfs_key current_insert;
        struct btrfs_key last_insert;
+       struct radix_tree_root pending_del_radix;
        struct radix_tree_root pinned_radix;
        u64 last_inode_alloc;
        u64 last_inode_alloc_dirid;
index e32ddff55b0ebb25d5cfddf05eaf57a532a633bb..758a62aba063d3f00102e88c8f674140b6ac5b66 100644 (file)
@@ -104,7 +104,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
        /* FIXME: don't be stupid */
        if (!btrfs_super_root(disk_super))
                return NULL;
-       INIT_RADIX_TREE(&fs_info->pinned_radix, GFP_KERNEL);
+       init_bit_radix(&fs_info->pinned_radix);
+       init_bit_radix(&fs_info->pending_del_radix);
        fs_info->running_transaction = NULL;
        fs_info->fs_root = root;
        fs_info->tree_root = tree_root;
index 369b960fce45b201e47a1d1fa8a5353154f4d3da..b14104276eea8159d128c5e37fe4a923054ac52b 100644 (file)
@@ -1,5 +1,4 @@
 #include <linux/module.h>
-#include <linux/radix-tree.h>
 #include "ctree.h"
 #include "disk-io.h"
 #include "print-tree.h"
@@ -12,15 +11,6 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, struct
                                 btrfs_root *extent_root);
 static int del_pending_extents(struct btrfs_trans_handle *trans, struct
                               btrfs_root *extent_root);
-/*
- * pending extents are blocks that we're trying to allocate in the extent
- * map while trying to grow the map because of other allocations.  To avoid
- * recursing, they are tagged in the radix tree and cleaned up after
- * other allocations are done.  The pending tag is also used in the same
- * manner for deletes.
- */
-#define CTREE_EXTENT_PENDING_DEL 0
-#define CTREE_EXTENT_PINNED 1
 
 static int inc_block_ref(struct btrfs_trans_handle *trans, struct btrfs_root
                         *root, u64 blocknr)
@@ -104,24 +94,21 @@ int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, struct
                               btrfs_root *root)
 {
-       struct buffer_head *gang[8];
+       unsigned long gang[8];
        u64 first = 0;
        int ret;
        int i;
+       struct radix_tree_root *pinned_radix = &root->fs_info->pinned_radix;
 
        while(1) {
-               ret = radix_tree_gang_lookup_tag(&root->fs_info->pinned_radix,
-                                            (void **)gang, 0,
-                                            ARRAY_SIZE(gang),
-                                            CTREE_EXTENT_PINNED);
+               ret = find_first_radix_bit(pinned_radix, gang,
+                                          ARRAY_SIZE(gang));
                if (!ret)
                        break;
                if (!first)
-                       first = gang[0]->b_blocknr;
+                       first = gang[0];
                for (i = 0; i < ret; i++) {
-                       radix_tree_delete(&root->fs_info->pinned_radix,
-                                         gang[i]->b_blocknr);
-                       brelse(gang[i]);
+                       clear_radix_bit(pinned_radix, gang[i]);
                }
        }
        if (root->fs_info->last_insert.objectid > first)
@@ -161,29 +148,27 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, struct
        return 0;
 }
 
-static int pin_down_block(struct btrfs_root *root, u64 blocknr, int tag)
+static int pin_down_block(struct btrfs_root *root, u64 blocknr, int pending)
 {
        int err;
-       struct buffer_head *bh = sb_getblk(root->fs_info->sb, blocknr);
        struct btrfs_header *header;
-       BUG_ON(!bh);
-
-       header = btrfs_buffer_header(bh);
-       if (btrfs_header_generation(header) ==
-           root->fs_info->running_transaction->transid) {
-               return 0;
-       }
-
-       err = radix_tree_insert(&root->fs_info->pinned_radix,
-                               blocknr, bh);
-       if (err && err != -EEXIST) {
-               BUG();
-               return err;
-       }
-       if (err == -EEXIST)
+       struct buffer_head *bh;
+
+       bh = sb_find_get_block(root->fs_info->sb, blocknr);
+       if (bh) {
+               header = btrfs_buffer_header(bh);
+               if (btrfs_header_generation(header) ==
+                   root->fs_info->running_transaction->transid) {
+                       brelse(bh);
+                       return 0;
+               }
                brelse(bh);
-       radix_tree_tag_set(&root->fs_info->pinned_radix, blocknr,
-                          tag);
+       }
+       if (pending)
+               err = set_radix_bit(&root->fs_info->pending_del_radix, blocknr);
+       else
+               err = set_radix_bit(&root->fs_info->pinned_radix, blocknr);
+       BUG_ON(err);
        return 0;
 }
 
@@ -225,8 +210,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
                u64 super_blocks_used;
 
                if (pin) {
-                       ret = pin_down_block(root, blocknr,
-                                            CTREE_EXTENT_PINNED);
+                       ret = pin_down_block(root, blocknr, 0);
                        BUG_ON(ret);
                }
 
@@ -255,25 +239,26 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
        int ret;
        int wret;
        int err = 0;
-       struct buffer_head *gang[4];
+       unsigned long gang[4];
        int i;
-       struct radix_tree_root *radix = &extent_root->fs_info->pinned_radix;
+       struct radix_tree_root *pending_radix;
+       struct radix_tree_root *pinned_radix;
+
+       pending_radix = &extent_root->fs_info->pending_del_radix;
+       pinned_radix = &extent_root->fs_info->pinned_radix;
 
        while(1) {
-               ret = radix_tree_gang_lookup_tag(
-                                       &extent_root->fs_info->pinned_radix,
-                                       (void **)gang, 0,
-                                       ARRAY_SIZE(gang),
-                                       CTREE_EXTENT_PENDING_DEL);
+               ret = find_first_radix_bit(pending_radix, gang,
+                                          ARRAY_SIZE(gang));
                if (!ret)
                        break;
                for (i = 0; i < ret; i++) {
-                       radix_tree_tag_set(radix, gang[i]->b_blocknr,
-                                          CTREE_EXTENT_PINNED);
-                       radix_tree_tag_clear(radix, gang[i]->b_blocknr,
-                                            CTREE_EXTENT_PENDING_DEL);
+                       wret = set_radix_bit(pinned_radix, gang[i]);
+                       BUG_ON(wret);
+                       wret = clear_radix_bit(pending_radix, gang[i]);
+                       BUG_ON(wret);
                        wret = __free_extent(trans, extent_root,
-                                            gang[i]->b_blocknr, 1, 0);
+                                            gang[i], 1, 0);
                        if (wret)
                                err = wret;
                }
@@ -294,7 +279,7 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
 
        if (root == extent_root) {
                t = find_tree_block(root, blocknr);
-               pin_down_block(root, blocknr, CTREE_EXTENT_PENDING_DEL);
+               pin_down_block(root, blocknr, 1);
                return 0;
        }
        ret = __free_extent(trans, root, blocknr, num_blocks, pin);
@@ -393,7 +378,7 @@ check_pending:
        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,
+               if (test_radix_bit(&root->fs_info->pinned_radix,
                                      test_block)) {
                        search_start = test_block + 1;
                        goto check_failed;