Skip to content

Commit

Permalink
Btrfs: rescan for qgroups
Browse files Browse the repository at this point in the history
If qgroup tracking is out of sync, a rescan operation can be started. It
iterates the complete extent tree and recalculates all qgroup tracking data.
This is an expensive operation and should not be used unless required.

A filesystem under rescan can still be umounted. The rescan continues on the
next mount.  Status information is provided with a separate ioctl while a
rescan operation is in progress.

Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
  • Loading branch information
Jan Schmidt authored and Josef Bacik committed May 6, 2013
1 parent 46b665c commit 2f23203
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 35 deletions.
17 changes: 11 additions & 6 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1021,9 +1021,9 @@ struct btrfs_block_group_item {
*/
#define BTRFS_QGROUP_STATUS_FLAG_ON (1ULL << 0)
/*
* SCANNING is set during the initialization phase
* RESCAN is set during the initialization phase
*/
#define BTRFS_QGROUP_STATUS_FLAG_SCANNING (1ULL << 1)
#define BTRFS_QGROUP_STATUS_FLAG_RESCAN (1ULL << 1)
/*
* Some qgroup entries are known to be out of date,
* either because the configuration has changed in a way that
Expand Down Expand Up @@ -1052,7 +1052,7 @@ struct btrfs_qgroup_status_item {
* only used during scanning to record the progress
* of the scan. It contains a logical address
*/
__le64 scan;
__le64 rescan;
} __attribute__ ((__packed__));

struct btrfs_qgroup_info_item {
Expand Down Expand Up @@ -1603,6 +1603,11 @@ struct btrfs_fs_info {
/* used by btrfs_qgroup_record_ref for an efficient tree traversal */
u64 qgroup_seq;

/* qgroup rescan items */
struct mutex qgroup_rescan_lock; /* protects the progress item */
struct btrfs_key qgroup_rescan_progress;
struct btrfs_workers qgroup_rescan_workers;

/* filesystem state */
unsigned long fs_state;

Expand Down Expand Up @@ -2886,8 +2891,8 @@ BTRFS_SETGET_FUNCS(qgroup_status_version, struct btrfs_qgroup_status_item,
version, 64);
BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item,
flags, 64);
BTRFS_SETGET_FUNCS(qgroup_status_scan, struct btrfs_qgroup_status_item,
scan, 64);
BTRFS_SETGET_FUNCS(qgroup_status_rescan, struct btrfs_qgroup_status_item,
rescan, 64);

/* btrfs_qgroup_info_item */
BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item,
Expand Down Expand Up @@ -3828,7 +3833,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info);
int btrfs_quota_disable(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info);
int btrfs_quota_rescan(struct btrfs_fs_info *fs_info);
int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info);
int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 src, u64 dst);
int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
Expand Down
5 changes: 5 additions & 0 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1976,6 +1976,7 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
btrfs_stop_workers(&fs_info->caching_workers);
btrfs_stop_workers(&fs_info->readahead_workers);
btrfs_stop_workers(&fs_info->flush_workers);
btrfs_stop_workers(&fs_info->qgroup_rescan_workers);
}

/* helper to cleanup tree roots */
Expand Down Expand Up @@ -2267,6 +2268,7 @@ int open_ctree(struct super_block *sb,
fs_info->qgroup_seq = 1;
fs_info->quota_enabled = 0;
fs_info->pending_quota_state = 0;
mutex_init(&fs_info->qgroup_rescan_lock);

btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);
btrfs_init_free_cluster(&fs_info->data_alloc_cluster);
Expand Down Expand Up @@ -2476,6 +2478,8 @@ int open_ctree(struct super_block *sb,
btrfs_init_workers(&fs_info->readahead_workers, "readahead",
fs_info->thread_pool_size,
&fs_info->generic_worker);
btrfs_init_workers(&fs_info->qgroup_rescan_workers, "qgroup-rescan", 1,
&fs_info->generic_worker);

/*
* endios are largely parallel and should have a very
Expand Down Expand Up @@ -2510,6 +2514,7 @@ int open_ctree(struct super_block *sb,
ret |= btrfs_start_workers(&fs_info->caching_workers);
ret |= btrfs_start_workers(&fs_info->readahead_workers);
ret |= btrfs_start_workers(&fs_info->flush_workers);
ret |= btrfs_start_workers(&fs_info->qgroup_rescan_workers);
if (ret) {
err = -ENOMEM;
goto fail_sb_buffer;
Expand Down
83 changes: 69 additions & 14 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3701,12 +3701,10 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
}

down_write(&root->fs_info->subvol_sem);
if (sa->cmd != BTRFS_QUOTA_CTL_RESCAN) {
trans = btrfs_start_transaction(root->fs_info->tree_root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
}
trans = btrfs_start_transaction(root->fs_info->tree_root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
}

switch (sa->cmd) {
Expand All @@ -3716,9 +3714,6 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
case BTRFS_QUOTA_CTL_DISABLE:
ret = btrfs_quota_disable(trans, root->fs_info);
break;
case BTRFS_QUOTA_CTL_RESCAN:
ret = btrfs_quota_rescan(root->fs_info);
break;
default:
ret = -EINVAL;
break;
Expand All @@ -3727,11 +3722,9 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
if (copy_to_user(arg, sa, sizeof(*sa)))
ret = -EFAULT;

if (trans) {
err = btrfs_commit_transaction(trans, root->fs_info->tree_root);
if (err && !ret)
ret = err;
}
err = btrfs_commit_transaction(trans, root->fs_info->tree_root);
if (err && !ret)
ret = err;
out:
kfree(sa);
up_write(&root->fs_info->subvol_sem);
Expand Down Expand Up @@ -3886,6 +3879,64 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
return ret;
}

static long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg)
{
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
struct btrfs_ioctl_quota_rescan_args *qsa;
int ret;

if (!capable(CAP_SYS_ADMIN))
return -EPERM;

ret = mnt_want_write_file(file);
if (ret)
return ret;

qsa = memdup_user(arg, sizeof(*qsa));
if (IS_ERR(qsa)) {
ret = PTR_ERR(qsa);
goto drop_write;
}

if (qsa->flags) {
ret = -EINVAL;
goto out;
}

ret = btrfs_qgroup_rescan(root->fs_info);

out:
kfree(qsa);
drop_write:
mnt_drop_write_file(file);
return ret;
}

static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg)
{
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
struct btrfs_ioctl_quota_rescan_args *qsa;
int ret = 0;

if (!capable(CAP_SYS_ADMIN))
return -EPERM;

qsa = kzalloc(sizeof(*qsa), GFP_NOFS);
if (!qsa)
return -ENOMEM;

if (root->fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
qsa->flags = 1;
qsa->progress = root->fs_info->qgroup_rescan_progress.objectid;
}

if (copy_to_user(arg, qsa, sizeof(*qsa)))
ret = -EFAULT;

kfree(qsa);
return ret;
}

static long btrfs_ioctl_set_received_subvol(struct file *file,
void __user *arg)
{
Expand Down Expand Up @@ -4124,6 +4175,10 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_qgroup_create(file, argp);
case BTRFS_IOC_QGROUP_LIMIT:
return btrfs_ioctl_qgroup_limit(file, argp);
case BTRFS_IOC_QUOTA_RESCAN:
return btrfs_ioctl_quota_rescan(file, argp);
case BTRFS_IOC_QUOTA_RESCAN_STATUS:
return btrfs_ioctl_quota_rescan_status(file, argp);
case BTRFS_IOC_DEV_REPLACE:
return btrfs_ioctl_dev_replace(root, argp);
case BTRFS_IOC_GET_FSLABEL:
Expand Down
Loading

0 comments on commit 2f23203

Please sign in to comment.