summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r--fs/btrfs/ioctl.c644
1 files changed, 506 insertions, 138 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 2616f7ed479..ac37040e426 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -50,6 +50,7 @@
#include "print-tree.h"
#include "volumes.h"
#include "locking.h"
+#include "inode-map.h"
/* Mask out flags that are inappropriate for the given type of inode. */
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
@@ -242,7 +243,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);
}
- trans = btrfs_join_transaction(root, 1);
+ trans = btrfs_join_transaction(root);
BUG_ON(IS_ERR(trans));
ret = btrfs_update_inode(trans, root, inode);
@@ -281,8 +282,9 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock(&fs_info->fs_devices->device_list_mutex);
- list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(device, &fs_info->fs_devices->devices,
+ dev_list) {
if (!device->bdev)
continue;
q = bdev_get_queue(device->bdev);
@@ -292,7 +294,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)
minlen);
}
}
- mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+ rcu_read_unlock();
if (!num_devices)
return -EOPNOTSUPP;
@@ -329,8 +331,7 @@ static noinline int create_subvol(struct btrfs_root *root,
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
u64 index = 0;
- ret = btrfs_find_free_objectid(NULL, root->fs_info->tree_root,
- 0, &objectid);
+ ret = btrfs_find_free_objectid(root->fs_info->tree_root, &objectid);
if (ret) {
dput(parent);
return ret;
@@ -413,8 +414,7 @@ static noinline int create_subvol(struct btrfs_root *root,
btrfs_record_root_in_trans(trans, new_root);
- ret = btrfs_create_subvol_root(trans, new_root, new_dirid,
- BTRFS_I(dir)->block_group);
+ ret = btrfs_create_subvol_root(trans, new_root, new_dirid);
/*
* insert the directory item
*/
@@ -422,7 +422,7 @@ static noinline int create_subvol(struct btrfs_root *root,
BUG_ON(ret);
ret = btrfs_insert_dir_item(trans, root,
- name, namelen, dir->i_ino, &key,
+ name, namelen, dir, &key,
BTRFS_FT_DIR, index);
if (ret)
goto fail;
@@ -433,7 +433,7 @@ static noinline int create_subvol(struct btrfs_root *root,
ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
objectid, root->root_key.objectid,
- dir->i_ino, index, name, namelen);
+ btrfs_ino(dir), index, name, namelen);
BUG_ON(ret);
@@ -655,6 +655,107 @@ out_unlock:
return error;
}
+/*
+ * When we're defragging a range, we don't want to kick it off again
+ * if it is really just waiting for delalloc to send it down.
+ * If we find a nice big extent or delalloc range for the bytes in the
+ * file you want to defrag, we return 0 to let you know to skip this
+ * part of the file
+ */
+static int check_defrag_in_cache(struct inode *inode, u64 offset, int thresh)
+{
+ 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;
+ u64 end;
+
+ read_lock(&em_tree->lock);
+ em = lookup_extent_mapping(em_tree, offset, PAGE_CACHE_SIZE);
+ read_unlock(&em_tree->lock);
+
+ if (em) {
+ end = extent_map_end(em);
+ free_extent_map(em);
+ if (end - offset > thresh)
+ return 0;
+ }
+ /* if we already have a nice delalloc here, just stop */
+ thresh /= 2;
+ end = count_range_bits(io_tree, &offset, offset + thresh,
+ thresh, EXTENT_DELALLOC, 1);
+ if (end >= thresh)
+ return 0;
+ return 1;
+}
+
+/*
+ * helper function to walk through a file and find extents
+ * newer than a specific transid, and smaller than thresh.
+ *
+ * This is used by the defragging code to find new and small
+ * extents
+ */
+static int find_new_extents(struct btrfs_root *root,
+ struct inode *inode, u64 newer_than,
+ u64 *off, int thresh)
+{
+ struct btrfs_path *path;
+ struct btrfs_key min_key;
+ struct btrfs_key max_key;
+ struct extent_buffer *leaf;
+ struct btrfs_file_extent_item *extent;
+ int type;
+ int ret;
+ u64 ino = btrfs_ino(inode);
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ min_key.objectid = ino;
+ min_key.type = BTRFS_EXTENT_DATA_KEY;
+ min_key.offset = *off;
+
+ max_key.objectid = ino;
+ max_key.type = (u8)-1;
+ max_key.offset = (u64)-1;
+
+ path->keep_locks = 1;
+
+ while(1) {
+ ret = btrfs_search_forward(root, &min_key, &max_key,
+ path, 0, newer_than);
+ if (ret != 0)
+ goto none;
+ if (min_key.objectid != ino)
+ goto none;
+ if (min_key.type != BTRFS_EXTENT_DATA_KEY)
+ goto none;
+
+ leaf = path->nodes[0];
+ extent = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_file_extent_item);
+
+ type = btrfs_file_extent_type(leaf, extent);
+ if (type == BTRFS_FILE_EXTENT_REG &&
+ btrfs_file_extent_num_bytes(leaf, extent) < thresh &&
+ check_defrag_in_cache(inode, min_key.offset, thresh)) {
+ *off = min_key.offset;
+ btrfs_free_path(path);
+ return 0;
+ }
+
+ if (min_key.offset == (u64)-1)
+ goto none;
+
+ min_key.offset++;
+ btrfs_release_path(path);
+ }
+none:
+ btrfs_free_path(path);
+ return -ENOENT;
+}
+
static int should_defrag_range(struct inode *inode, u64 start, u64 len,
int thresh, u64 *last_len, u64 *skip,
u64 *defrag_end)
@@ -664,10 +765,6 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
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
@@ -726,27 +823,176 @@ static int should_defrag_range(struct inode *inode, u64 start, u64 len,
return ret;
}
-static int btrfs_defrag_file(struct file *file,
- struct btrfs_ioctl_defrag_range_args *range)
+/*
+ * it doesn't do much good to defrag one or two pages
+ * at a time. This pulls in a nice chunk of pages
+ * to COW and defrag.
+ *
+ * It also makes sure the delalloc code has enough
+ * dirty data to avoid making new small extents as part
+ * of the defrag
+ *
+ * It's a good idea to start RA on this range
+ * before calling this.
+ */
+static int cluster_pages_for_defrag(struct inode *inode,
+ struct page **pages,
+ unsigned long start_index,
+ int num_pages)
{
- struct inode *inode = fdentry(file)->d_inode;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ unsigned long file_end;
+ u64 isize = i_size_read(inode);
+ u64 page_start;
+ u64 page_end;
+ int ret;
+ int i;
+ int i_done;
struct btrfs_ordered_extent *ordered;
- struct page *page;
+ struct extent_state *cached_state = NULL;
+
+ if (isize == 0)
+ return 0;
+ file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
+
+ ret = btrfs_delalloc_reserve_space(inode,
+ num_pages << PAGE_CACHE_SHIFT);
+ if (ret)
+ return ret;
+again:
+ ret = 0;
+ i_done = 0;
+
+ /* step one, lock all the pages */
+ for (i = 0; i < num_pages; i++) {
+ struct page *page;
+ page = grab_cache_page(inode->i_mapping,
+ start_index + i);
+ if (!page)
+ break;
+
+ if (!PageUptodate(page)) {
+ btrfs_readpage(NULL, page);
+ lock_page(page);
+ if (!PageUptodate(page)) {
+ unlock_page(page);
+ page_cache_release(page);
+ ret = -EIO;
+ break;
+ }
+ }
+ isize = i_size_read(inode);
+ file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
+ if (!isize || page->index > file_end ||
+ page->mapping != inode->i_mapping) {
+ /* whoops, we blew past eof, skip this page */
+ unlock_page(page);
+ page_cache_release(page);
+ break;
+ }
+ pages[i] = page;
+ i_done++;
+ }
+ if (!i_done || ret)
+ goto out;
+
+ if (!(inode->i_sb->s_flags & MS_ACTIVE))
+ goto out;
+
+ /*
+ * so now we have a nice long stream of locked
+ * and up to date pages, lets wait on them
+ */
+ for (i = 0; i < i_done; i++)
+ wait_on_page_writeback(pages[i]);
+
+ page_start = page_offset(pages[0]);
+ page_end = page_offset(pages[i_done - 1]) + PAGE_CACHE_SIZE;
+
+ lock_extent_bits(&BTRFS_I(inode)->io_tree,
+ page_start, page_end - 1, 0, &cached_state,
+ GFP_NOFS);
+ ordered = btrfs_lookup_first_ordered_extent(inode, page_end - 1);
+ if (ordered &&
+ ordered->file_offset + ordered->len > page_start &&
+ ordered->file_offset < page_end) {
+ btrfs_put_ordered_extent(ordered);
+ unlock_extent_cached(&BTRFS_I(inode)->io_tree,
+ page_start, page_end - 1,
+ &cached_state, GFP_NOFS);
+ for (i = 0; i < i_done; i++) {
+ unlock_page(pages[i]);
+ page_cache_release(pages[i]);
+ }
+ btrfs_wait_ordered_range(inode, page_start,
+ page_end - page_start);
+ goto again;
+ }
+ if (ordered)
+ btrfs_put_ordered_extent(ordered);
+
+ clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
+ page_end - 1, EXTENT_DIRTY | EXTENT_DELALLOC |
+ EXTENT_DO_ACCOUNTING, 0, 0, &cached_state,
+ GFP_NOFS);
+
+ if (i_done != num_pages) {
+ atomic_inc(&BTRFS_I(inode)->outstanding_extents);
+ btrfs_delalloc_release_space(inode,
+ (num_pages - i_done) << PAGE_CACHE_SHIFT);
+ }
+
+
+ btrfs_set_extent_delalloc(inode, page_start, page_end - 1,
+ &cached_state);
+
+ unlock_extent_cached(&BTRFS_I(inode)->io_tree,
+ page_start, page_end - 1, &cached_state,
+ GFP_NOFS);
+
+ for (i = 0; i < i_done; i++) {
+ clear_page_dirty_for_io(pages[i]);
+ ClearPageChecked(pages[i]);
+ set_page_extent_mapped(pages[i]);
+ set_page_dirty(pages[i]);
+ unlock_page(pages[i]);
+ page_cache_release(pages[i]);
+ }
+ return i_done;
+out:
+ for (i = 0; i < i_done; i++) {
+ unlock_page(pages[i]);
+ page_cache_release(pages[i]);
+ }
+ btrfs_delalloc_release_space(inode, num_pages << PAGE_CACHE_SHIFT);
+ return ret;
+
+}
+
+int btrfs_defrag_file(struct inode *inode, struct file *file,
+ struct btrfs_ioctl_defrag_range_args *range,
+ u64 newer_than, unsigned long max_to_defrag)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_super_block *disk_super;
+ struct file_ra_state *ra = NULL;
unsigned long last_index;
- unsigned long ra_pages = root->fs_info->bdi.ra_pages;
- unsigned long total_read = 0;
u64 features;
- u64 page_start;
- u64 page_end;
u64 last_len = 0;
u64 skip = 0;
u64 defrag_end = 0;
+ u64 newer_off = range->start;
+ int newer_left = 0;
unsigned long i;
int ret;
+ int defrag_count = 0;
int compress_type = BTRFS_COMPRESS_ZLIB;
+ int extent_thresh = range->extent_thresh;
+ int newer_cluster = (256 * 1024) >> PAGE_CACHE_SHIFT;
+ u64 new_align = ~((u64)128 * 1024 - 1);
+ struct page **pages = NULL;
+
+ if (extent_thresh == 0)
+ extent_thresh = 256 * 1024;
if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS) {
if (range->compress_type > BTRFS_COMPRESS_TYPES)
@@ -758,6 +1004,27 @@ static int btrfs_defrag_file(struct file *file,
if (inode->i_size == 0)
return 0;
+ /*
+ * if we were not given a file, allocate a readahead
+ * context
+ */
+ if (!file) {
+ ra = kzalloc(sizeof(*ra), GFP_NOFS);
+ if (!ra)
+ return -ENOMEM;
+ file_ra_state_init(ra, inode->i_mapping);
+ } else {
+ ra = &file->f_ra;
+ }
+
+ pages = kmalloc(sizeof(struct page *) * newer_cluster,
+ GFP_NOFS);
+ if (!pages) {
+ ret = -ENOMEM;
+ goto out_ra;
+ }
+
+ /* find the last page to defrag */
if (range->start + range->len > range->start) {
last_index = min_t(u64, inode->i_size - 1,
range->start + range->len - 1) >> PAGE_CACHE_SHIFT;
@@ -765,11 +1032,37 @@ static int btrfs_defrag_file(struct file *file,
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,
+ if (newer_than) {
+ ret = find_new_extents(root, inode, newer_than,
+ &newer_off, 64 * 1024);
+ if (!ret) {
+ range->start = newer_off;
+ /*
+ * we always align our defrag to help keep
+ * the extents in the file evenly spaced
+ */
+ i = (newer_off & new_align) >> PAGE_CACHE_SHIFT;
+ newer_left = newer_cluster;
+ } else
+ goto out_ra;
+ } else {
+ i = range->start >> PAGE_CACHE_SHIFT;
+ }
+ if (!max_to_defrag)
+ max_to_defrag = last_index - 1;
+
+ while (i <= last_index && defrag_count < max_to_defrag) {
+ /*
+ * make sure we stop running if someone unmounts
+ * the FS
+ */
+ if (!(inode->i_sb->s_flags & MS_ACTIVE))
+ break;
+
+ if (!newer_than &&
+ !should_defrag_range(inode, (u64)i << PAGE_CACHE_SHIFT,
PAGE_CACHE_SIZE,
- range->extent_thresh,
+ extent_thresh,
&last_len, &skip,
&defrag_end)) {
unsigned long next;
@@ -781,92 +1074,39 @@ static int btrfs_defrag_file(struct file *file,
i = max(i + 1, next);
continue;
}
-
- if (total_read % ra_pages == 0) {
- btrfs_force_ra(inode->i_mapping, &file->f_ra, file, i,
- min(last_index, i + ra_pages - 1));
- }
- total_read++;
- mutex_lock(&inode->i_mutex);
if (range->flags & BTRFS_DEFRAG_RANGE_COMPRESS)
BTRFS_I(inode)->force_compress = compress_type;
- ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
- if (ret)
- goto err_unlock;
-again:
- if (inode->i_size == 0 ||
- i > ((inode->i_size - 1) >> PAGE_CACHE_SHIFT)) {
- ret = 0;
- goto err_reservations;
- }
+ btrfs_force_ra(inode->i_mapping, ra, file, i, newer_cluster);
- page = grab_cache_page(inode->i_mapping, i);
- if (!page) {
- ret = -ENOMEM;
- goto err_reservations;
- }
-
- if (!PageUptodate(page)) {
- btrfs_readpage(NULL, page);
- lock_page(page);
- if (!PageUptodate(page)) {
- unlock_page(page);
- page_cache_release(page);
- ret = -EIO;
- goto err_reservations;
- }
- }
-
- if (page->mapping != inode->i_mapping) {
- unlock_page(page);
- page_cache_release(page);
- goto again;
- }
+ ret = cluster_pages_for_defrag(inode, pages, i, newer_cluster);
+ if (ret < 0)
+ goto out_ra;
- wait_on_page_writeback(page);
+ defrag_count += ret;
+ balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret);
+ i += ret;
- if (PageDirty(page)) {
- btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE);
- goto loop_unlock;
- }
-
- page_start = (u64)page->index << PAGE_CACHE_SHIFT;
- page_end = page_start + PAGE_CACHE_SIZE - 1;
- lock_extent(io_tree, page_start, page_end, GFP_NOFS);
+ if (newer_than) {
+ if (newer_off == (u64)-1)
+ break;
- ordered = btrfs_lookup_ordered_extent(inode, page_start);
- if (ordered) {
- unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
- unlock_page(page);
- page_cache_release(page);
- btrfs_start_ordered_extent(inode, ordered, 1);
- btrfs_put_ordered_extent(ordered);
- goto again;
+ newer_off = max(newer_off + 1,
+ (u64)i << PAGE_CACHE_SHIFT);
+
+ ret = find_new_extents(root, inode,
+ newer_than, &newer_off,
+ 64 * 1024);
+ if (!ret) {
+ range->start = newer_off;
+ i = (newer_off & new_align) >> PAGE_CACHE_SHIFT;
+ newer_left = newer_cluster;
+ } else {
+ break;
+ }
+ } else {
+ i++;
}
- set_page_extent_mapped(page);
-
- /*
- * this makes sure page_mkwrite is called on the
- * page if it is dirtied again later
- */
- clear_page_dirty_for_io(page);
- clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start,
- page_end, EXTENT_DIRTY | EXTENT_DELALLOC |
- EXTENT_DO_ACCOUNTING, GFP_NOFS);
-
- btrfs_set_extent_delalloc(inode, page_start, page_end, NULL);
- ClearPageChecked(page);
- set_page_dirty(page);
- unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
-
-loop_unlock:
- unlock_page(page);
- page_cache_release(page);
- mutex_unlock(&inode->i_mutex);
-
- balance_dirty_pages_ratelimited_nr(inode->i_mapping, 1);
- i++;
}
if ((range->flags & BTRFS_DEFRAG_RANGE_START_IO))
@@ -898,12 +1138,14 @@ loop_unlock:
btrfs_set_super_incompat_flags(disk_super, features);
}
- return 0;
+ if (!file)
+ kfree(ra);
+ return defrag_count;
-err_reservations:
- btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE);
-err_unlock:
- mutex_unlock(&inode->i_mutex);
+out_ra:
+ if (!file)
+ kfree(ra);
+ kfree(pages);
return ret;
}
@@ -1129,7 +1371,7 @@ static noinline int btrfs_ioctl_subvol_getflags(struct file *file,
int ret = 0;
u64 flags = 0;
- if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
+ if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
return -EINVAL;
down_read(&root->fs_info->subvol_sem);
@@ -1156,7 +1398,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file,
if (root->fs_info->sb->s_flags & MS_RDONLY)
return -EROFS;
- if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID)
+ if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID)
return -EINVAL;
if (copy_from_user(&flags, arg, sizeof(flags)))
@@ -1279,7 +1521,6 @@ static noinline int copy_to_sk(struct btrfs_root *root,
int nritems;
int i;
int slot;
- int found = 0;
int ret = 0;
leaf = path->nodes[0];
@@ -1326,7 +1567,7 @@ static noinline int copy_to_sk(struct btrfs_root *root,
item_off, item_len);
*sk_offset += item_len;
}
- found++;
+ (*num_found)++;
if (*num_found >= sk->nr_items)
break;
@@ -1345,7 +1586,6 @@ advance_key:
} else
ret = 1;
overflow:
- *num_found += found;
return ret;
}
@@ -1402,7 +1642,7 @@ static noinline int search_ioctl(struct inode *inode,
}
ret = copy_to_sk(root, path, &key, sk, args->buf,
&sk_offset, &num_found);
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
if (ret || num_found >= sk->nr_items)
break;
@@ -1509,7 +1749,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
if (key.offset == BTRFS_FIRST_FREE_OBJECTID)
break;
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
key.objectid = key.offset;
key.offset = (u64)-1;
dirid = key.objectid;
@@ -1639,7 +1879,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
goto out_dput;
}
- if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
+ if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {
err = -EINVAL;
goto out_dput;
}
@@ -1757,7 +1997,10 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
/* the rest are all set to zero by kzalloc */
range->len = (u64)-1;
}
- ret = btrfs_defrag_file(file, range);
+ ret = btrfs_defrag_file(fdentry(file)->d_inode, file,
+ range, 0, 0);
+ if (ret > 0)
+ ret = 0;
kfree(range);
break;
default:
@@ -1809,6 +2052,75 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
return ret;
}
+static long btrfs_ioctl_fs_info(struct btrfs_root *root, void __user *arg)
+{
+ struct btrfs_ioctl_fs_info_args fi_args;
+ struct btrfs_device *device;
+ struct btrfs_device *next;
+ struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ fi_args.num_devices = fs_devices->num_devices;
+ fi_args.max_id = 0;
+ memcpy(&fi_args.fsid, root->fs_info->fsid, sizeof(fi_args.fsid));
+
+ mutex_lock(&fs_devices->device_list_mutex);
+ list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
+ if (device->devid > fi_args.max_id)
+ fi_args.max_id = device->devid;
+ }
+ mutex_unlock(&fs_devices->device_list_mutex);
+
+ if (copy_to_user(arg, &fi_args, sizeof(fi_args)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
+{
+ struct btrfs_ioctl_dev_info_args *di_args;
+ struct btrfs_device *dev;
+ struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
+ int ret = 0;
+ char *s_uuid = NULL;
+ char empty_uuid[BTRFS_UUID_SIZE] = {0};
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ di_args = memdup_user(arg, sizeof(*di_args));
+ if (IS_ERR(di_args))
+ return PTR_ERR(di_args);
+
+ if (memcmp(empty_uuid, di_args->uuid, BTRFS_UUID_SIZE) != 0)
+ s_uuid = di_args->uuid;
+
+ mutex_lock(&fs_devices->device_list_mutex);
+ dev = btrfs_find_device(root, di_args->devid, s_uuid, NULL);
+ mutex_unlock(&fs_devices->device_list_mutex);
+
+ if (!dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ di_args->devid = dev->devid;
+ di_args->bytes_used = dev->bytes_used;
+ di_args->total_bytes = dev->total_bytes;
+ memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
+ strncpy(di_args->path, dev->name, sizeof(di_args->path));
+
+out:
+ if (ret == 0 && copy_to_user(arg, di_args, sizeof(*di_args)))
+ ret = -EFAULT;
+
+ kfree(di_args);
+ return ret;
+}
+
static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
u64 off, u64 olen, u64 destoff)
{
@@ -1925,7 +2237,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
}
/* clone data */
- key.objectid = src->i_ino;
+ key.objectid = btrfs_ino(src);
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = 0;
@@ -1952,7 +2264,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
btrfs_item_key_to_cpu(leaf, &key, slot);
if (btrfs_key_type(&key) > BTRFS_EXTENT_DATA_KEY ||
- key.objectid != src->i_ino)
+ key.objectid != btrfs_ino(src))
break;
if (btrfs_key_type(&key) == BTRFS_EXTENT_DATA_KEY) {
@@ -1988,14 +2300,14 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
datal = btrfs_file_extent_ram_bytes(leaf,
extent);
}
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
if (key.offset + datal <= off ||
key.offset >= off+len)
goto next;
memcpy(&new_key, &key, sizeof(new_key));
- new_key.objectid = inode->i_ino;
+ new_key.objectid = btrfs_ino(inode);
if (off <= key.offset)
new_key.offset = key.offset + destoff - off;
else
@@ -2049,7 +2361,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
ret = btrfs_inc_extent_ref(trans, root,
disko, diskl, 0,
root->root_key.objectid,
- inode->i_ino,
+ btrfs_ino(inode),
new_key.offset - datao);
BUG_ON(ret);
}
@@ -2098,7 +2410,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
}
btrfs_mark_buffer_dirty(leaf);
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
@@ -2119,12 +2431,12 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
btrfs_end_transaction(trans, root);
}
next:
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
key.offset++;
}
ret = 0;
out:
- btrfs_release_path(root, path);
+ btrfs_release_path(path);
unlock_extent(&BTRFS_I(src)->io_tree, off, off+len, GFP_NOFS);
out_unlock:
mutex_unlock(&src->i_mutex);
@@ -2177,12 +2489,10 @@ static long btrfs_ioctl_trans_start(struct file *file)
if (ret)
goto out;
- mutex_lock(&root->fs_info->trans_mutex);
- root->fs_info->open_ioctl_trans++;
- mutex_unlock(&root->fs_info->trans_mutex);
+ atomic_inc(&root->fs_info->open_ioctl_trans);
ret = -ENOMEM;
- trans = btrfs_start_ioctl_transaction(root, 0);
+ trans = btrfs_start_ioctl_transaction(root);
if (IS_ERR(trans))
goto out_drop;
@@ -2190,9 +2500,7 @@ static long btrfs_ioctl_trans_start(struct file *file)
return 0;
out_drop:
- mutex_lock(&root->fs_info->trans_mutex);
- root->fs_info->open_ioctl_trans--;
- mutex_unlock(&root->fs_info->trans_mutex);
+ atomic_dec(&root->fs_info->open_ioctl_trans);
mnt_drop_write(file->f_path.mnt);
out:
return ret;
@@ -2426,9 +2734,7 @@ long btrfs_ioctl_trans_end(struct file *file)
btrfs_end_transaction(trans, root);
- mutex_lock(&root->fs_info->trans_mutex);
- root->fs_info->open_ioctl_trans--;
- mutex_unlock(&root->fs_info->trans_mutex);
+ atomic_dec(&root->fs_info->open_ioctl_trans);
mnt_drop_write(file->f_path.mnt);
return 0;
@@ -2471,6 +2777,58 @@ static noinline long btrfs_ioctl_wait_sync(struct file *file, void __user *argp)
return btrfs_wait_for_commit(root, transid);
}
+static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg)
+{
+ int ret;
+ struct btrfs_ioctl_scrub_args *sa;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ sa = memdup_user(arg, sizeof(*sa));
+ if (IS_ERR(sa))
+ return PTR_ERR(sa);
+
+ ret = btrfs_scrub_dev(root, sa->devid, sa->start, sa->end,
+ &sa->progress, sa->flags & BTRFS_SCRUB_READONLY);
+
+ if (copy_to_user(arg, sa, sizeof(*sa)))
+ ret = -EFAULT;
+
+ kfree(sa);
+ return ret;
+}
+
+static long btrfs_ioctl_scrub_cancel(struct btrfs_root *root, void __user *arg)
+{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return btrfs_scrub_cancel(root);
+}
+
+static long btrfs_ioctl_scrub_progress(struct btrfs_root *root,
+ void __user *arg)
+{
+ struct btrfs_ioctl_scrub_args *sa;
+ int ret;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ sa = memdup_user(arg, sizeof(*sa));
+ if (IS_ERR(sa))
+ return PTR_ERR(sa);
+
+ ret = btrfs_scrub_progress(root, sa->devid, &sa->progress);
+
+ if (copy_to_user(arg, sa, sizeof(*sa)))
+ ret = -EFAULT;
+
+ kfree(sa);
+ return ret;
+}
+
long btrfs_ioctl(struct file *file, unsigned int
cmd, unsigned long arg)
{
@@ -2510,6 +2868,10 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_add_dev(root, argp);
case BTRFS_IOC_RM_DEV:
return btrfs_ioctl_rm_dev(root, argp);
+ case BTRFS_IOC_FS_INFO:
+ return btrfs_ioctl_fs_info(root, argp);
+ case BTRFS_IOC_DEV_INFO:
+ return btrfs_ioctl_dev_info(root, argp);
case BTRFS_IOC_BALANCE:
return btrfs_balance(root->fs_info->dev_root);
case BTRFS_IOC_CLONE:
@@ -2533,6 +2895,12 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_start_sync(file, argp);
case BTRFS_IOC_WAIT_SYNC:
return btrfs_ioctl_wait_sync(file, argp);
+ case BTRFS_IOC_SCRUB:
+ return btrfs_ioctl_scrub(root, argp);
+ case BTRFS_IOC_SCRUB_CANCEL:
+ return btrfs_ioctl_scrub_cancel(root, argp);
+ case BTRFS_IOC_SCRUB_PROGRESS:
+ return btrfs_ioctl_scrub_progress(root, argp);
}
return -ENOTTY;