Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e701a32 authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: add new defrag-range ioctl.



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: default avatarChris Mason <chris.mason@oracle.com>
parent 940100a4
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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;
};

+0 −1
Original line number Diff line number Diff line
@@ -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:
+8 −3
Original line number Diff line number Diff line
@@ -379,7 +379,8 @@ static noinline int compress_file_range(struct inode *inode,
	 * 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,9 +484,11 @@ static noinline int compress_file_range(struct inode *inode,
		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);
+73 −10
Original line number Diff line number Diff line
@@ -476,13 +476,18 @@ static noinline int btrfs_mksubvol(struct path *parent,
}

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;

	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 = 0;
	}

	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 @@ static int btrfs_defrag_file(struct file *file)
		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 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
	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:
+31 −0
Original line number Diff line number Diff line
@@ -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, \