Btrfs: search for an allocation hint while filling file COW
authorChris Mason <chris.mason@oracle.com>
Fri, 18 Sep 2009 20:07:03 +0000 (16:07 -0400)
committerChris Mason <chris.mason@oracle.com>
Fri, 18 Sep 2009 20:08:52 +0000 (16:08 -0400)
The allocator has some nice knobs for sending hints about where
to try and allocate new blocks, but when we're doing file allocations
we're not sending any hint at all.

This commit adds a simple extent map search to see if we can
quickly and easily find a hint for the allocator.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/inode.c

index 5bc7a0d325e7f6a00849c74c794cb168b12c4b4c..2c726b7b9faa17af725b8a5b8319148758b7dd91 100644 (file)
@@ -366,6 +366,54 @@ out:
        return em;
 }
 
+/**
+ * search_extent_mapping - find a nearby extent map
+ * @tree:      tree to lookup in
+ * @start:     byte offset to start the search
+ * @len:       length of the lookup range
+ *
+ * Find and return the first extent_map struct in @tree that intersects the
+ * [start, len] range.
+ *
+ * If one can't be found, any nearby extent may be returned
+ */
+struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
+                                        u64 start, u64 len)
+{
+       struct extent_map *em;
+       struct rb_node *rb_node;
+       struct rb_node *prev = NULL;
+       struct rb_node *next = NULL;
+
+       rb_node = __tree_search(&tree->map, start, &prev, &next);
+       if (!rb_node && prev) {
+               em = rb_entry(prev, struct extent_map, rb_node);
+               goto found;
+       }
+       if (!rb_node && next) {
+               em = rb_entry(next, struct extent_map, rb_node);
+               goto found;
+       }
+       if (!rb_node) {
+               em = NULL;
+               goto out;
+       }
+       if (IS_ERR(rb_node)) {
+               em = ERR_PTR(PTR_ERR(rb_node));
+               goto out;
+       }
+       em = rb_entry(rb_node, struct extent_map, rb_node);
+       goto found;
+
+       em = NULL;
+       goto out;
+
+found:
+       atomic_inc(&em->refs);
+out:
+       return em;
+}
+
 /**
  * remove_extent_mapping - removes an extent_map from the extent tree
  * @tree:      extent tree to remove from
index d3d442f4bbbdad3d29a1d6e5fddf80f52770c371..ab6d74b6e6477dcfb1bf65df494c5749cc8d80d9 100644 (file)
@@ -60,4 +60,6 @@ void free_extent_map(struct extent_map *em);
 int __init extent_map_init(void);
 void extent_map_exit(void);
 int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len);
+struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
+                                        u64 start, u64 len);
 #endif
index 941f1b71cd2212b00d53109f83d73c1417e807fd..81ba6654c3326dd38ba42944d8c121959b24b892 100644 (file)
@@ -726,6 +726,15 @@ static noinline int cow_file_range(struct inode *inode,
        BUG_ON(disk_num_bytes >
               btrfs_super_total_bytes(&root->fs_info->super_copy));
 
+
+       read_lock(&BTRFS_I(inode)->extent_tree.lock);
+       em = search_extent_mapping(&BTRFS_I(inode)->extent_tree,
+                                  start, num_bytes);
+       if (em) {
+               alloc_hint = em->block_start;
+               free_extent_map(em);
+       }
+       read_unlock(&BTRFS_I(inode)->extent_tree.lock);
        btrfs_drop_extent_cache(inode, start, start + num_bytes - 1, 0);
 
        while (disk_num_bytes > 0) {
@@ -738,7 +747,6 @@ static noinline int cow_file_range(struct inode *inode,
                em = alloc_extent_map(GFP_NOFS);
                em->start = start;
                em->orig_start = em->start;
-
                ram_size = ins.offset;
                em->len = ins.offset;