Btrfs: add new defrag-range ioctl.
Chris Mason [Thu, 11 Mar 2010 14:42:04 +0000 (09:42 -0500)]
The btrfs defrag ioctl was limited to doing the entire file.  This
commit adds a new interface that can defrag a specific range inside
the file.

It can also force compression on the file, allowing you to selectively
compress individual files after they were created, even when mount -o
compress isn't turned on.

Signed-off-by: Chris Mason <chris.mason@oracle.com>

fs/btrfs/btrfs_inode.h
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ioctl.h

index 3f1f50d..7a4dee1 100644 (file)
@@ -153,6 +153,11 @@ struct btrfs_inode {
        unsigned ordered_data_close:1;
        unsigned dummy_inode:1;
 
+       /*
+        * always compress this one file
+        */
+       unsigned force_compress:1;
+
        struct inode vfs_inode;
 };
 
index 1166b15..3a36b1f 100644 (file)
@@ -1184,7 +1184,6 @@ struct btrfs_root {
 #define BTRFS_INODE_NOATIME            (1 << 9)
 #define BTRFS_INODE_DIRSYNC            (1 << 10)
 
-
 /* some macros to generate set/get funcs for the struct fields.  This
  * assumes there is a lefoo_to_cpu for every type, so lets make a simple
  * one for u8:
index 7d10d1c..3657925 100644 (file)
@@ -379,7 +379,8 @@ again:
         * change at any time if we discover bad compression ratios.
         */
        if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&
-           btrfs_test_opt(root, COMPRESS)) {
+           (btrfs_test_opt(root, COMPRESS) ||
+            (BTRFS_I(inode)->force_compress))) {
                WARN_ON(pages);
                pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS);
 
@@ -483,8 +484,10 @@ again:
                nr_pages_ret = 0;
 
                /* flag the file so we don't compress in the future */
-               if (!btrfs_test_opt(root, FORCE_COMPRESS))
+               if (!btrfs_test_opt(root, FORCE_COMPRESS) &&
+                   !(BTRFS_I(inode)->force_compress)) {
                        BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;
+               }
        }
        if (will_compress) {
                *num_added += 1;
@@ -1211,7 +1214,8 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
        else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)
                ret = run_delalloc_nocow(inode, locked_page, start, end,
                                         page_started, 0, nr_written);
-       else if (!btrfs_test_opt(root, COMPRESS))
+       else if (!btrfs_test_opt(root, COMPRESS) &&
+                !(BTRFS_I(inode)->force_compress))
                ret = cow_file_range(inode, locked_page, start, end,
                                      page_started, nr_written, 1);
        else
@@ -3639,6 +3643,7 @@ static noinline void init_btrfs_i(struct inode *inode)
        bi->index_cnt = (u64)-1;
        bi->last_unlink_trans = 0;
        bi->ordered_data_close = 0;
+       bi->force_compress = 0;
        extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
        extent_io_tree_init(&BTRFS_I(inode)->io_tree,
                             inode->i_mapping, GFP_NOFS);
index 3a89cd7..d866b46 100644 (file)
@@ -476,13 +476,18 @@ out_unlock:
 }
 
 static int should_defrag_range(struct inode *inode, u64 start, u64 len,
-                              u64 *last_len, u64 *skip, u64 *defrag_end)
+                              int thresh, u64 *last_len, u64 *skip,
+                              u64 *defrag_end)
 {
        struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
        struct extent_map *em = NULL;
        struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        int ret = 1;
 
+
+       if (thresh == 0)
+               thresh = 256 * 1024;
+
        /*
         * make sure that once we start defragging and extent, we keep on
         * defragging it
@@ -517,8 +522,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
        /*
         * we hit a real extent, if it is big don't bother defragging it again
         */
-       if ((*last_len == 0 || *last_len >= 256 * 1024) &&
-           em->len >= 256 * 1024)
+       if ((*last_len == 0 || *last_len >= thresh) && em->len >= thresh)
                ret = 0;
 
        /*
@@ -542,7 +546,8 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
        return ret;
 }
 
-static int btrfs_defrag_file(struct file *file)
+static int btrfs_defrag_file(struct file *file,
+                            struct btrfs_ioctl_defrag_range_args *range)
 {
        struct inode *inode = fdentry(file)->d_inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -563,11 +568,19 @@ static int btrfs_defrag_file(struct file *file)
        if (inode->i_size == 0)
                return 0;
 
-       last_index = (inode->i_size - 1) >> PAGE_CACHE_SHIFT;
-       i = 0;
+       if (range->start + range->len > range->start) {
+               last_index = min_t(u64, inode->i_size - 1,
+                        range->start + range->len - 1) >> PAGE_CACHE_SHIFT;
+       } else {
+               last_index = (inode->i_size - 1) >> PAGE_CACHE_SHIFT;
+       }
+
+       i = range->start >> PAGE_CACHE_SHIFT;
        while (i <= last_index) {
                if (!should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
-                                       PAGE_CACHE_SIZE, &last_len, &skip,
+                                       PAGE_CACHE_SIZE,
+                                       range->extent_thresh,
+                                       &last_len, &skip,
                                        &defrag_end)) {
                        unsigned long next;
                        /*
@@ -585,6 +598,8 @@ static int btrfs_defrag_file(struct file *file)
                }
                total_read++;
                mutex_lock(&inode->i_mutex);
+               if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)
+                       BTRFS_I(inode)->force_compress = 1;
 
                ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE);
                if (ret) {
@@ -673,6 +688,28 @@ loop_unlock:
                i++;
        }
 
+       if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO))
+               filemap_flush(inode->i_mapping);
+
+       if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
+               /* the filemap_flush will queue IO into the worker threads, but
+                * we have to make sure the IO is actually started and that
+                * ordered extents get created before we return
+                */
+               atomic_inc(&root->fs_info->async_submit_draining);
+               while (atomic_read(&root->fs_info->nr_async_submits) ||
+                     atomic_read(&root->fs_info->async_delalloc_pages)) {
+                       wait_event(root->fs_info->async_submit_wait,
+                          (atomic_read(&root->fs_info->nr_async_submits) == 0 &&
+                           atomic_read(&root->fs_info->async_delalloc_pages) == 0));
+               }
+               atomic_dec(&root->fs_info->async_submit_draining);
+
+               mutex_lock(&inode->i_mutex);
+               BTRFS_I(inode)->force_compress = 0;
+               mutex_unlock(&inode->i_mutex);
+       }
+
        return 0;
 
 err_reservations:
@@ -1284,10 +1321,11 @@ out:
        return err;
 }
 
-static int btrfs_ioctl_defrag(struct file *file)
+static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
 {
        struct inode *inode = fdentry(file)->d_inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
+       struct btrfs_ioctl_defrag_range_args *range;
        int ret;
 
        ret = mnt_want_write(file->f_path.mnt);
@@ -1308,7 +1346,30 @@ static int btrfs_ioctl_defrag(struct file *file)
                        ret = -EINVAL;
                        goto out;
                }
-               btrfs_defrag_file(file);
+
+               range = kzalloc(sizeof(*range), GFP_KERNEL);
+               if (!range) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               if (argp) {
+                       if (copy_from_user(range, argp,
+                                          sizeof(*range))) {
+                               ret = -EFAULT;
+                               kfree(range);
+                       }
+                       /* compression requires us to start the IO */
+                       if ((range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)) {
+                               range->flags |= BTRFS_DEFRAG_RANGE_START_IO;
+                               range->extent_thresh = (u32)-1;
+                       }
+               } else {
+                       /* the rest are all set to zero by kzalloc */
+                       range->len = (u64)-1;
+               }
+               btrfs_defrag_file(file, range);
+               kfree(range);
                break;
        }
 out:
@@ -1831,7 +1892,9 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_DEFAULT_SUBVOL:
                return btrfs_ioctl_default_subvol(file, argp);
        case BTRFS_IOC_DEFRAG:
-               return btrfs_ioctl_defrag(file);
+               return btrfs_ioctl_defrag(file, NULL);
+       case BTRFS_IOC_DEFRAG_RANGE:
+               return btrfs_ioctl_defrag(file, argp);
        case BTRFS_IOC_RESIZE:
                return btrfs_ioctl_resize(root, argp);
        case BTRFS_IOC_ADD_DEV:
index f1923e0..2d64a65 100644 (file)
@@ -99,6 +99,35 @@ struct btrfs_ioctl_clone_range_args {
   __u64 dest_offset;
 };
 
+/* flags for the defrag range ioctl */
+#define BTRFS_DEFRAG_RANGE_COMPRESS 1
+#define BTRFS_DEFRAG_RANGE_START_IO 2
+
+struct btrfs_ioctl_defrag_range_args {
+       /* start of the defrag operation */
+       __u64 start;
+
+       /* number of bytes to defrag, use (u64)-1 to say all */
+       __u64 len;
+
+       /*
+        * flags for the operation, which can include turning
+        * on compression for this one defrag
+        */
+       __u64 flags;
+
+       /*
+        * any extent bigger than this will be considered
+        * already defragged.  Use 0 to take the kernel default
+        * Use 1 to say every single extent must be rewritten
+        */
+       __u32 extent_thresh;
+
+       /* spare for later */
+       __u32 unused[5];
+};
+
+
 #define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
                                   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \
@@ -130,6 +159,8 @@ struct btrfs_ioctl_clone_range_args {
                                   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
                                struct btrfs_ioctl_vol_args)
+#define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \
+                               struct btrfs_ioctl_defrag_range_args)
 #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
                                   struct btrfs_ioctl_search_args)
 #define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \