Add support for defragging files via btrfsctl -d. Avoid OOM on extent tree
authorChris Mason <chris.mason@oracle.com>
Mon, 10 Sep 2007 23:58:16 +0000 (19:58 -0400)
committerDavid Woodhouse <dwmw2@hera.kernel.org>
Mon, 10 Sep 2007 23:58:16 +0000 (19:58 -0400)
defrag.

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

index 89e282c35ce203d8eeaa3b4be54d703550afb7ed..b41f48ade41982143caf1dc8c574e6d4fb706ee6 100644 (file)
@@ -217,6 +217,9 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                       root->fs_info->generation);
                WARN_ON(1);
        }
+       if (buffer_defrag_done(parent))
+               return 0;
+
        parent_node = btrfs_buffer_node(parent);
        parent_nritems = btrfs_header_nritems(&parent_node->header);
        parent_level = btrfs_header_level(&parent_node->header);
@@ -274,6 +277,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                *last_ret = search_start;
                if (parent_level == 1)
                        clear_buffer_defrag(tmp_bh);
+               set_buffer_defrag_done(tmp_bh);
                brelse(tmp_bh);
        }
        return err;
index da6bb72750f251c29b071e17e71dd244bee331a3..714fa877438b20f1837d06b710acc4599e6a63f4 100644 (file)
 enum btrfs_bh_state_bits {
        BH_Checked = BH_PrivateStart,
        BH_Defrag,
+       BH_DefragDone,
 };
 BUFFER_FNS(Checked, checked);
 BUFFER_FNS(Defrag, defrag);
+BUFFER_FNS(DefragDone, defrag_done);
 
 static inline struct btrfs_node *btrfs_buffer_node(struct buffer_head *bh)
 {
index 8ad6f8efc5a003a039c43f47753e278ce182393e..33f7a18dddf7b406994ecbfc3951687d4bc73da5 100644 (file)
 #include <linux/blkdev.h>
 #include "extent_map.h"
 
+/* temporary define until extent_map moves out of btrfs */
+struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
+                                      unsigned long extra_flags,
+                                      void (*ctor)(void *, struct kmem_cache *,
+                                                   unsigned long));
+
 static struct kmem_cache *extent_map_cache;
 static struct kmem_cache *extent_state_cache;
 
@@ -32,14 +38,12 @@ struct tree_entry {
 
 void __init extent_map_init(void)
 {
-       extent_map_cache = kmem_cache_create("extent_map",
-                                           sizeof(struct extent_map), 0,
-                                           SLAB_RECLAIM_ACCOUNT |
+       extent_map_cache = btrfs_cache_create("extent_map",
+                                           sizeof(struct extent_map),
                                            SLAB_DESTROY_BY_RCU,
                                            NULL);
-       extent_state_cache = kmem_cache_create("extent_state",
-                                           sizeof(struct extent_state), 0,
-                                           SLAB_RECLAIM_ACCOUNT |
+       extent_state_cache = btrfs_cache_create("extent_state",
+                                           sizeof(struct extent_state),
                                            SLAB_DESTROY_BY_RCU,
                                            NULL);
 }
index 64710fa77d01500f789c547541cb97baaff33bdd..6b3e4404dc6a0e088a1224cf6357de84886c62fb 100644 (file)
@@ -1904,6 +1904,70 @@ fail:
        return ret;
 }
 
+static unsigned long force_ra(struct address_space *mapping,
+                             struct file_ra_state *ra, struct file *file,
+                             pgoff_t offset, pgoff_t last_index)
+{
+       pgoff_t req_size;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+       req_size = last_index - offset + 1;
+       offset = page_cache_readahead(mapping, ra, file, offset, req_size);
+       return offset;
+#else
+       req_size = min(last_index - offset + 1, (pgoff_t)128);
+       page_cache_sync_readahead(mapping, ra, file, offset, req_size);
+       return offset + req_size;
+#endif
+}
+
+int btrfs_defrag_file(struct file *file) {
+       struct inode *inode = file->f_path.dentry->d_inode;
+       struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+       struct page *page;
+       unsigned long last_index;
+       unsigned long ra_index = 0;
+       u64 page_start;
+       u64 page_end;
+       unsigned long i;
+
+       mutex_lock(&inode->i_mutex);
+       last_index = inode->i_size >> PAGE_CACHE_SHIFT;
+       for (i = 0; i <= last_index; i++) {
+               if (i == ra_index) {
+                       ra_index = force_ra(inode->i_mapping, &file->f_ra,
+                                           file, ra_index, last_index);
+               }
+               page = grab_cache_page(inode->i_mapping, i);
+               if (!page)
+                       goto out_unlock;
+               if (!PageUptodate(page)) {
+                       btrfs_readpage(NULL, page);
+                       lock_page(page);
+                       if (!PageUptodate(page)) {
+                               unlock_page(page);
+                               page_cache_release(page);
+                               goto out_unlock;
+                       }
+               }
+               page_start = page->index << PAGE_CACHE_SHIFT;
+               page_end = page_start + PAGE_CACHE_SIZE - 1;
+
+               lock_extent(em_tree, page_start, page_end, GFP_NOFS);
+               set_extent_delalloc(em_tree, page_start,
+                                   page_end, GFP_NOFS);
+               unlock_extent(em_tree, page_start, page_end, GFP_NOFS);
+               set_page_dirty(page);
+               unlock_page(page);
+               page_cache_release(page);
+               balance_dirty_pages_ratelimited_nr(inode->i_mapping, 1);
+       }
+
+out_unlock:
+       mutex_unlock(&inode->i_mutex);
+       return 0;
+}
+
 int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
                cmd, unsigned long arg)
 {
@@ -1948,10 +2012,14 @@ int btrfs_ioctl(struct inode *inode, struct file *filp, unsigned int
                break;
 
        case BTRFS_IOC_DEFRAG:
-               mutex_lock(&root->fs_info->fs_mutex);
-               btrfs_defrag_root(root, 0);
-               btrfs_defrag_root(root->fs_info->extent_root, 0);
-               mutex_unlock(&root->fs_info->fs_mutex);
+               if (S_ISDIR(inode->i_mode)) {
+                       mutex_lock(&root->fs_info->fs_mutex);
+                       btrfs_defrag_root(root, 0);
+                       btrfs_defrag_root(root->fs_info->extent_root, 0);
+                       mutex_unlock(&root->fs_info->fs_mutex);
+               } else if (S_ISREG(inode->i_mode)) {
+                       btrfs_defrag_file(filp);
+               }
                ret = 0;
                break;
        default:
@@ -2018,7 +2086,7 @@ void btrfs_destroy_cachep(void)
                kmem_cache_destroy(btrfs_path_cachep);
 }
 
-static struct kmem_cache *cache_create(const char *name, size_t size,
+struct kmem_cache *btrfs_cache_create(const char *name, size_t size,
                                       unsigned long extra_flags,
                                       void (*ctor)(void *, struct kmem_cache *,
                                                    unsigned long))
@@ -2033,27 +2101,28 @@ static struct kmem_cache *cache_create(const char *name, size_t size,
 
 int btrfs_init_cachep(void)
 {
-       btrfs_inode_cachep = cache_create("btrfs_inode_cache",
+       btrfs_inode_cachep = btrfs_cache_create("btrfs_inode_cache",
                                          sizeof(struct btrfs_inode),
                                          0, init_once);
        if (!btrfs_inode_cachep)
                goto fail;
-       btrfs_trans_handle_cachep = cache_create("btrfs_trans_handle_cache",
-                                            sizeof(struct btrfs_trans_handle),
-                                            0, NULL);
+       btrfs_trans_handle_cachep =
+                       btrfs_cache_create("btrfs_trans_handle_cache",
+                                          sizeof(struct btrfs_trans_handle),
+                                          0, NULL);
        if (!btrfs_trans_handle_cachep)
                goto fail;
-       btrfs_transaction_cachep = cache_create("btrfs_transaction_cache",
+       btrfs_transaction_cachep = btrfs_cache_create("btrfs_transaction_cache",
                                             sizeof(struct btrfs_transaction),
                                             0, NULL);
        if (!btrfs_transaction_cachep)
                goto fail;
-       btrfs_path_cachep = cache_create("btrfs_path_cache",
+       btrfs_path_cachep = btrfs_cache_create("btrfs_path_cache",
                                         sizeof(struct btrfs_transaction),
                                         0, NULL);
        if (!btrfs_path_cachep)
                goto fail;
-       btrfs_bit_radix_cachep = cache_create("btrfs_radix", 256,
+       btrfs_bit_radix_cachep = btrfs_cache_create("btrfs_radix", 256,
                                              SLAB_DESTROY_BY_RCU, NULL);
        if (!btrfs_bit_radix_cachep)
                goto fail;
index 35fd20d2464557f11ad2e73c8e5c17d4b8f0a171..420597127ed18a8a9e96282b8406a04a387896aa 100644 (file)
@@ -113,6 +113,8 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
        }
        WARN_ON(*level < 0);
        WARN_ON(*level >= BTRFS_MAX_LEVEL);
+       clear_buffer_defrag(path->nodes[*level]);
+       clear_buffer_defrag_done(path->nodes[*level]);
        btrfs_block_release(root, path->nodes[*level]);
        path->nodes[*level] = NULL;
        *level += 1;
@@ -143,6 +145,7 @@ static int defrag_walk_up(struct btrfs_trans_handle *trans,
                        return 0;
                } else {
                        clear_buffer_defrag(path->nodes[*level]);
+                       clear_buffer_defrag_done(path->nodes[*level]);
                        btrfs_block_release(root, path->nodes[*level]);
                        path->nodes[*level] = NULL;
                        *level = i + 1;