Commit 2f232036 authored by Jan Schmidt's avatar Jan Schmidt Committed by Josef Bacik

Btrfs: rescan for qgroups

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: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent 46b665ce
...@@ -1021,9 +1021,9 @@ struct btrfs_block_group_item { ...@@ -1021,9 +1021,9 @@ struct btrfs_block_group_item {
*/ */
#define BTRFS_QGROUP_STATUS_FLAG_ON (1ULL << 0) #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, * Some qgroup entries are known to be out of date,
* either because the configuration has changed in a way that * either because the configuration has changed in a way that
...@@ -1052,7 +1052,7 @@ struct btrfs_qgroup_status_item { ...@@ -1052,7 +1052,7 @@ struct btrfs_qgroup_status_item {
* only used during scanning to record the progress * only used during scanning to record the progress
* of the scan. It contains a logical address * of the scan. It contains a logical address
*/ */
__le64 scan; __le64 rescan;
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
struct btrfs_qgroup_info_item { struct btrfs_qgroup_info_item {
...@@ -1603,6 +1603,11 @@ struct btrfs_fs_info { ...@@ -1603,6 +1603,11 @@ struct btrfs_fs_info {
/* used by btrfs_qgroup_record_ref for an efficient tree traversal */ /* used by btrfs_qgroup_record_ref for an efficient tree traversal */
u64 qgroup_seq; 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 */ /* filesystem state */
unsigned long fs_state; unsigned long fs_state;
...@@ -2886,8 +2891,8 @@ BTRFS_SETGET_FUNCS(qgroup_status_version, struct btrfs_qgroup_status_item, ...@@ -2886,8 +2891,8 @@ BTRFS_SETGET_FUNCS(qgroup_status_version, struct btrfs_qgroup_status_item,
version, 64); version, 64);
BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item, BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item,
flags, 64); flags, 64);
BTRFS_SETGET_FUNCS(qgroup_status_scan, struct btrfs_qgroup_status_item, BTRFS_SETGET_FUNCS(qgroup_status_rescan, struct btrfs_qgroup_status_item,
scan, 64); rescan, 64);
/* btrfs_qgroup_info_item */ /* btrfs_qgroup_info_item */
BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item, BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item,
...@@ -3828,7 +3833,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans, ...@@ -3828,7 +3833,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info); struct btrfs_fs_info *fs_info);
int btrfs_quota_disable(struct btrfs_trans_handle *trans, int btrfs_quota_disable(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info); 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, int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, u64 src, u64 dst); struct btrfs_fs_info *fs_info, u64 src, u64 dst);
int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans,
......
...@@ -1976,6 +1976,7 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info) ...@@ -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->caching_workers);
btrfs_stop_workers(&fs_info->readahead_workers); btrfs_stop_workers(&fs_info->readahead_workers);
btrfs_stop_workers(&fs_info->flush_workers); btrfs_stop_workers(&fs_info->flush_workers);
btrfs_stop_workers(&fs_info->qgroup_rescan_workers);
} }
/* helper to cleanup tree roots */ /* helper to cleanup tree roots */
...@@ -2267,6 +2268,7 @@ int open_ctree(struct super_block *sb, ...@@ -2267,6 +2268,7 @@ int open_ctree(struct super_block *sb,
fs_info->qgroup_seq = 1; fs_info->qgroup_seq = 1;
fs_info->quota_enabled = 0; fs_info->quota_enabled = 0;
fs_info->pending_quota_state = 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->meta_alloc_cluster);
btrfs_init_free_cluster(&fs_info->data_alloc_cluster); btrfs_init_free_cluster(&fs_info->data_alloc_cluster);
...@@ -2476,6 +2478,8 @@ int open_ctree(struct super_block *sb, ...@@ -2476,6 +2478,8 @@ int open_ctree(struct super_block *sb,
btrfs_init_workers(&fs_info->readahead_workers, "readahead", btrfs_init_workers(&fs_info->readahead_workers, "readahead",
fs_info->thread_pool_size, fs_info->thread_pool_size,
&fs_info->generic_worker); &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 * endios are largely parallel and should have a very
...@@ -2510,6 +2514,7 @@ int open_ctree(struct super_block *sb, ...@@ -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->caching_workers);
ret |= btrfs_start_workers(&fs_info->readahead_workers); ret |= btrfs_start_workers(&fs_info->readahead_workers);
ret |= btrfs_start_workers(&fs_info->flush_workers); ret |= btrfs_start_workers(&fs_info->flush_workers);
ret |= btrfs_start_workers(&fs_info->qgroup_rescan_workers);
if (ret) { if (ret) {
err = -ENOMEM; err = -ENOMEM;
goto fail_sb_buffer; goto fail_sb_buffer;
......
...@@ -3701,12 +3701,10 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) ...@@ -3701,12 +3701,10 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
} }
down_write(&root->fs_info->subvol_sem); down_write(&root->fs_info->subvol_sem);
if (sa->cmd != BTRFS_QUOTA_CTL_RESCAN) { trans = btrfs_start_transaction(root->fs_info->tree_root, 2);
trans = btrfs_start_transaction(root->fs_info->tree_root, 2); if (IS_ERR(trans)) {
if (IS_ERR(trans)) { ret = PTR_ERR(trans);
ret = PTR_ERR(trans); goto out;
goto out;
}
} }
switch (sa->cmd) { switch (sa->cmd) {
...@@ -3716,9 +3714,6 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) ...@@ -3716,9 +3714,6 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
case BTRFS_QUOTA_CTL_DISABLE: case BTRFS_QUOTA_CTL_DISABLE:
ret = btrfs_quota_disable(trans, root->fs_info); ret = btrfs_quota_disable(trans, root->fs_info);
break; break;
case BTRFS_QUOTA_CTL_RESCAN:
ret = btrfs_quota_rescan(root->fs_info);
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; break;
...@@ -3727,11 +3722,9 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) ...@@ -3727,11 +3722,9 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
if (copy_to_user(arg, sa, sizeof(*sa))) if (copy_to_user(arg, sa, sizeof(*sa)))
ret = -EFAULT; ret = -EFAULT;
if (trans) { err = btrfs_commit_transaction(trans, root->fs_info->tree_root);
err = btrfs_commit_transaction(trans, root->fs_info->tree_root); if (err && !ret)
if (err && !ret) ret = err;
ret = err;
}
out: out:
kfree(sa); kfree(sa);
up_write(&root->fs_info->subvol_sem); up_write(&root->fs_info->subvol_sem);
...@@ -3886,6 +3879,64 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg) ...@@ -3886,6 +3879,64 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
return ret; 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, static long btrfs_ioctl_set_received_subvol(struct file *file,
void __user *arg) void __user *arg)
{ {
...@@ -4124,6 +4175,10 @@ long btrfs_ioctl(struct file *file, unsigned int ...@@ -4124,6 +4175,10 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_qgroup_create(file, argp); return btrfs_ioctl_qgroup_create(file, argp);
case BTRFS_IOC_QGROUP_LIMIT: case BTRFS_IOC_QGROUP_LIMIT:
return btrfs_ioctl_qgroup_limit(file, argp); 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: case BTRFS_IOC_DEV_REPLACE:
return btrfs_ioctl_dev_replace(root, argp); return btrfs_ioctl_dev_replace(root, argp);
case BTRFS_IOC_GET_FSLABEL: case BTRFS_IOC_GET_FSLABEL:
......
This diff is collapsed.
...@@ -376,12 +376,18 @@ struct btrfs_ioctl_get_dev_stats { ...@@ -376,12 +376,18 @@ struct btrfs_ioctl_get_dev_stats {
#define BTRFS_QUOTA_CTL_ENABLE 1 #define BTRFS_QUOTA_CTL_ENABLE 1
#define BTRFS_QUOTA_CTL_DISABLE 2 #define BTRFS_QUOTA_CTL_DISABLE 2
#define BTRFS_QUOTA_CTL_RESCAN 3 #define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
struct btrfs_ioctl_quota_ctl_args { struct btrfs_ioctl_quota_ctl_args {
__u64 cmd; __u64 cmd;
__u64 status; __u64 status;
}; };
struct btrfs_ioctl_quota_rescan_args {
__u64 flags;
__u64 progress;
__u64 reserved[6];
};
struct btrfs_ioctl_qgroup_assign_args { struct btrfs_ioctl_qgroup_assign_args {
__u64 assign; __u64 assign;
__u64 src; __u64 src;
...@@ -520,6 +526,10 @@ struct btrfs_ioctl_send_args { ...@@ -520,6 +526,10 @@ struct btrfs_ioctl_send_args {
struct btrfs_ioctl_qgroup_create_args) struct btrfs_ioctl_qgroup_create_args)
#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \ #define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
struct btrfs_ioctl_qgroup_limit_args) struct btrfs_ioctl_qgroup_limit_args)
#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \
struct btrfs_ioctl_quota_rescan_args)
#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
struct btrfs_ioctl_quota_rescan_args)
#define BTRFS_IOC_GET_FSLABEL _IOR(BTRFS_IOCTL_MAGIC, 49, \ #define BTRFS_IOC_GET_FSLABEL _IOR(BTRFS_IOCTL_MAGIC, 49, \
char[BTRFS_LABEL_SIZE]) char[BTRFS_LABEL_SIZE])
#define BTRFS_IOC_SET_FSLABEL _IOW(BTRFS_IOCTL_MAGIC, 50, \ #define BTRFS_IOC_SET_FSLABEL _IOW(BTRFS_IOCTL_MAGIC, 50, \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment