summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/bcachefs/bcachefs_ioctl.h12
-rw-r--r--fs/bcachefs/chardev.c16
-rw-r--r--fs/bcachefs/chardev.h3
-rw-r--r--fs/bcachefs/extents.c51
-rw-r--r--fs/bcachefs/fs-ioctl.c91
-rw-r--r--fs/bcachefs/opts.c53
-rw-r--r--fs/bcachefs/opts.h5
-rw-r--r--fs/bcachefs/rebalance.c323
-rw-r--r--fs/bcachefs/rebalance.h3
-rw-r--r--fs/bcachefs/sb-errors_format.h4
-rw-r--r--fs/bcachefs/sysfs.c4
-rw-r--r--fs/bcachefs/xattr.c10
12 files changed, 387 insertions, 188 deletions
diff --git a/fs/bcachefs/bcachefs_ioctl.h b/fs/bcachefs/bcachefs_ioctl.h
index 5dc562f2a881..6043a8d93b1e 100644
--- a/fs/bcachefs/bcachefs_ioctl.h
+++ b/fs/bcachefs/bcachefs_ioctl.h
@@ -87,7 +87,9 @@ struct bch_ioctl_incremental {
#define BCH_IOCTL_DISK_RESIZE_JOURNAL_v2 _IOW(0xbc, 28, struct bch_ioctl_disk_resize_journal_v2)
#define BCH_IOCTL_SUBVOLUME_CREATE _IOW(0xbc, 16, struct bch_ioctl_subvolume)
+#define BCH_IOCTL_SUBVOLUME_CREATE_v2 _IOW(0xbc, 29, struct bch_ioctl_subvolume_v2)
#define BCH_IOCTL_SUBVOLUME_DESTROY _IOW(0xbc, 17, struct bch_ioctl_subvolume)
+#define BCH_IOCTL_SUBVOLUME_DESTROY_v2 _IOW(0xbc, 30, struct bch_ioctl_subvolume_v2)
#define BCH_IOCTL_DEV_USAGE_V2 _IOWR(0xbc, 18, struct bch_ioctl_dev_usage_v2)
@@ -451,6 +453,16 @@ struct bch_ioctl_subvolume {
__u64 src_ptr;
};
+struct bch_ioctl_subvolume_v2 {
+ __u32 flags;
+ __u32 dirfd;
+ __u16 mode;
+ __u16 pad[3];
+ __u64 dst_ptr;
+ __u64 src_ptr;
+ struct bch_ioctl_err_msg err;
+};
+
#define BCH_SUBVOL_SNAPSHOT_CREATE (1U << 0)
#define BCH_SUBVOL_SNAPSHOT_RO (1U << 1)
diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c
index f6f90d421f27..108c362a0ff4 100644
--- a/fs/bcachefs/chardev.c
+++ b/fs/bcachefs/chardev.c
@@ -187,7 +187,7 @@ static long bch2_ioctl_stop(struct bch_fs *c)
}
#endif
-static int copy_ioctl_err_msg(struct bch_ioctl_err_msg *dst, struct printbuf *src, int ret)
+int bch2_copy_ioctl_err_msg(struct bch_ioctl_err_msg *dst, struct printbuf *src, int ret)
{
if (ret) {
prt_printf(src, "error=%s", bch2_err_str(ret));
@@ -243,7 +243,7 @@ static long bch2_ioctl_disk_add_v2(struct bch_fs *c, struct bch_ioctl_disk_v2 ar
CLASS(printbuf, err)();
ret = bch2_dev_add(c, path, &err);
kfree(path);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
static long bch2_ioctl_disk_remove(struct bch_fs *c, struct bch_ioctl_disk arg)
@@ -287,7 +287,7 @@ static long bch2_ioctl_disk_remove_v2(struct bch_fs *c, struct bch_ioctl_disk_v2
CLASS(printbuf, err)();
int ret = bch2_dev_remove(c, ca, arg.flags, &err);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
static long bch2_ioctl_disk_online(struct bch_fs *c, struct bch_ioctl_disk arg)
@@ -333,7 +333,7 @@ static long bch2_ioctl_disk_online_v2(struct bch_fs *c, struct bch_ioctl_disk_v2
CLASS(printbuf, err)();
ret = bch2_dev_online(c, path, &err);
kfree(path);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
static long bch2_ioctl_disk_offline(struct bch_fs *c, struct bch_ioctl_disk arg)
@@ -377,7 +377,7 @@ static long bch2_ioctl_disk_offline_v2(struct bch_fs *c, struct bch_ioctl_disk_v
CLASS(printbuf, err)();
int ret = bch2_dev_offline(c, ca, arg.flags, &err);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
static long bch2_ioctl_disk_set_state(struct bch_fs *c,
@@ -429,7 +429,7 @@ static long bch2_ioctl_disk_set_state_v2(struct bch_fs *c,
ret = bch2_dev_set_state(c, ca, arg.new_state, arg.flags, &err);
err:
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
struct bch_data_ctx {
@@ -783,7 +783,7 @@ static long bch2_ioctl_disk_resize_v2(struct bch_fs *c,
CLASS(printbuf, err)();
int ret = bch2_dev_resize(c, ca, arg.nbuckets, &err);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
static long bch2_ioctl_disk_resize_journal(struct bch_fs *c,
@@ -825,7 +825,7 @@ static long bch2_ioctl_disk_resize_journal_v2(struct bch_fs *c,
CLASS(printbuf, err)();
int ret = bch2_set_nr_journal_buckets(c, ca, arg.nbuckets);
- return copy_ioctl_err_msg(&arg.err, &err, ret);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
}
#define BCH_IOCTL(_name, _argtype) \
diff --git a/fs/bcachefs/chardev.h b/fs/bcachefs/chardev.h
index 0f563ca53c36..cdd63e7618e9 100644
--- a/fs/bcachefs/chardev.h
+++ b/fs/bcachefs/chardev.h
@@ -4,6 +4,9 @@
#ifndef NO_BCACHEFS_FS
+struct printbuf;
+int bch2_copy_ioctl_err_msg(struct bch_ioctl_err_msg *, struct printbuf *, int);
+
long bch2_fs_ioctl(struct bch_fs *, unsigned, void __user *);
void bch2_fs_chardev_exit(struct bch_fs *);
diff --git a/fs/bcachefs/extents.c b/fs/bcachefs/extents.c
index 016242ffc98d..2fac21812e19 100644
--- a/fs/bcachefs/extents.c
+++ b/fs/bcachefs/extents.c
@@ -1305,57 +1305,6 @@ void bch2_extent_crc_unpacked_to_text(struct printbuf *out, struct bch_extent_cr
bch2_prt_compression_type(out, crc->compression_type);
}
-static void bch2_extent_rebalance_to_text(struct printbuf *out, struct bch_fs *c,
- const struct bch_extent_rebalance *r)
-{
- prt_str(out, "rebalance:");
-
- prt_printf(out, " replicas=%u", r->data_replicas);
- if (r->data_replicas_from_inode)
- prt_str(out, " (inode)");
-
- prt_str(out, " checksum=");
- bch2_prt_csum_opt(out, r->data_checksum);
- if (r->data_checksum_from_inode)
- prt_str(out, " (inode)");
-
- if (r->background_compression || r->background_compression_from_inode) {
- prt_str(out, " background_compression=");
- bch2_compression_opt_to_text(out, r->background_compression);
-
- if (r->background_compression_from_inode)
- prt_str(out, " (inode)");
- }
-
- if (r->background_target || r->background_target_from_inode) {
- prt_str(out, " background_target=");
- if (c)
- bch2_target_to_text(out, c, r->background_target);
- else
- prt_printf(out, "%u", r->background_target);
-
- if (r->background_target_from_inode)
- prt_str(out, " (inode)");
- }
-
- if (r->promote_target || r->promote_target_from_inode) {
- prt_str(out, " promote_target=");
- if (c)
- bch2_target_to_text(out, c, r->promote_target);
- else
- prt_printf(out, "%u", r->promote_target);
-
- if (r->promote_target_from_inode)
- prt_str(out, " (inode)");
- }
-
- if (r->erasure_code || r->erasure_code_from_inode) {
- prt_printf(out, " ec=%u", r->erasure_code);
- if (r->erasure_code_from_inode)
- prt_str(out, " (inode)");
- }
-}
-
void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
diff --git a/fs/bcachefs/fs-ioctl.c b/fs/bcachefs/fs-ioctl.c
index 8b9d3c7d1f57..20b46126c066 100644
--- a/fs/bcachefs/fs-ioctl.c
+++ b/fs/bcachefs/fs-ioctl.c
@@ -199,8 +199,9 @@ static int bch2_ioc_goingdown(struct bch_fs *c, u32 __user *arg)
return ret;
}
-static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
- struct bch_ioctl_subvolume arg)
+static long __bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume_v2 arg,
+ struct printbuf *err)
{
struct inode *dir;
struct bch_inode_info *inode;
@@ -214,13 +215,17 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
unsigned create_flags = BCH_CREATE_SUBVOL;
if (arg.flags & ~(BCH_SUBVOL_SNAPSHOT_CREATE|
- BCH_SUBVOL_SNAPSHOT_RO))
+ BCH_SUBVOL_SNAPSHOT_RO)) {
+ prt_str(err, "invalid flasg");
return -EINVAL;
+ }
if (!(arg.flags & BCH_SUBVOL_SNAPSHOT_CREATE) &&
(arg.src_ptr ||
- (arg.flags & BCH_SUBVOL_SNAPSHOT_RO)))
+ (arg.flags & BCH_SUBVOL_SNAPSHOT_RO))) {
+ prt_str(err, "invalid flasg");
return -EINVAL;
+ }
if (arg.flags & BCH_SUBVOL_SNAPSHOT_CREATE)
create_flags |= BCH_CREATE_SNAPSHOT;
@@ -243,6 +248,7 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
if (src_path.dentry->d_sb->s_fs_info != c) {
path_put(&src_path);
+ prt_str(err, "src_path not on dst filesystem");
error = -EXDEV;
goto err1;
}
@@ -258,6 +264,7 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
goto err2;
if (dst_dentry->d_sb->s_fs_info != c) {
+ prt_str(err, "dst_path not on dst filesystem");
error = -EXDEV;
goto err3;
}
@@ -276,6 +283,7 @@ static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
s_user_ns = dir->i_sb->s_user_ns;
if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
!kgid_has_mapping(s_user_ns, current_fsgid())) {
+ prt_str(err, "current uid/gid not mapped into fs namespace");
error = -EOVERFLOW;
goto err3;
}
@@ -315,8 +323,35 @@ err1:
return error;
}
-static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
- struct bch_ioctl_subvolume arg)
+static long bch2_ioctl_subvolume_create(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume arg)
+{
+ struct bch_ioctl_subvolume_v2 arg_v2 = {
+ .flags = arg.flags,
+ .dirfd = arg.dirfd,
+ .mode = arg.mode,
+ .dst_ptr = arg.dst_ptr,
+ .src_ptr = arg.src_ptr,
+ };
+
+ CLASS(printbuf, err)();
+ long ret = __bch2_ioctl_subvolume_create(c, filp, arg_v2, &err);
+ if (ret)
+ bch_err_msg(c, ret, "%s", err.buf);
+ return ret;
+}
+
+static long bch2_ioctl_subvolume_create_v2(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume_v2 arg)
+{
+ CLASS(printbuf, err)();
+ long ret = __bch2_ioctl_subvolume_create(c, filp, arg, &err);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
+}
+
+static long __bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume_v2 arg,
+ struct printbuf *err)
{
const char __user *name = (void __user *)(unsigned long)arg.dst_ptr;
struct path path;
@@ -350,6 +385,32 @@ err:
return ret;
}
+static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume arg)
+{
+ struct bch_ioctl_subvolume_v2 arg_v2 = {
+ .flags = arg.flags,
+ .dirfd = arg.dirfd,
+ .mode = arg.mode,
+ .dst_ptr = arg.dst_ptr,
+ .src_ptr = arg.src_ptr,
+ };
+
+ CLASS(printbuf, err)();
+ long ret = __bch2_ioctl_subvolume_destroy(c, filp, arg_v2, &err);
+ if (ret)
+ bch_err_msg(c, ret, "%s", err.buf);
+ return ret;
+}
+
+static long bch2_ioctl_subvolume_destroy_v2(struct bch_fs *c, struct file *filp,
+ struct bch_ioctl_subvolume_v2 arg)
+{
+ CLASS(printbuf, err)();
+ long ret = __bch2_ioctl_subvolume_destroy(c, filp, arg, &err);
+ return bch2_copy_ioctl_err_msg(&arg.err, &err, ret);
+}
+
long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct bch_inode_info *inode = file_bch_inode(file);
@@ -391,6 +452,15 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
}
+ case BCH_IOCTL_SUBVOLUME_CREATE_v2: {
+ struct bch_ioctl_subvolume_v2 i;
+
+ ret = copy_from_user(&i, (void __user *) arg, sizeof(i))
+ ? -EFAULT
+ : bch2_ioctl_subvolume_create_v2(c, file, i);
+ break;
+ }
+
case BCH_IOCTL_SUBVOLUME_DESTROY: {
struct bch_ioctl_subvolume i;
@@ -400,6 +470,15 @@ long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
}
+ case BCH_IOCTL_SUBVOLUME_DESTROY_v2: {
+ struct bch_ioctl_subvolume_v2 i;
+
+ ret = copy_from_user(&i, (void __user *) arg, sizeof(i))
+ ? -EFAULT
+ : bch2_ioctl_subvolume_destroy_v2(c, file, i);
+ break;
+ }
+
default:
ret = bch2_fs_ioctl(c, cmd, (void __user *) arg);
break;
diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c
index 9f5684ec056a..bd5faafc9aa7 100644
--- a/fs/bcachefs/opts.c
+++ b/fs/bcachefs/opts.c
@@ -518,7 +518,8 @@ void bch2_opts_to_text(struct printbuf *out,
}
}
-int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, enum bch_opt_id id, u64 v)
+int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, u64 inum, enum bch_opt_id id, u64 v,
+ bool change)
{
int ret = 0;
@@ -531,6 +532,8 @@ int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, enum bch_opt_id
case Opt_compression:
case Opt_background_compression:
ret = bch2_check_set_has_compressed_data(c, v);
+ if (ret)
+ return ret;
break;
case Opt_erasure_code:
if (v)
@@ -540,13 +543,26 @@ int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, enum bch_opt_id
break;
}
+ if (change &&
+ (id == Opt_foreground_target ||
+ id == Opt_background_target ||
+ id == Opt_promote_target ||
+ id == Opt_compression ||
+ id == Opt_background_compression ||
+ id == Opt_data_checksum ||
+ id == Opt_data_replicas)) {
+ ret = bch2_set_rebalance_needs_scan(c, inum);
+ if (ret)
+ return ret;
+ }
+
return ret;
}
int bch2_opts_hooks_pre_set(struct bch_fs *c)
{
for (unsigned i = 0; i < bch2_opts_nr; i++) {
- int ret = bch2_opt_hook_pre_set(c, NULL, i, bch2_opt_get_by_id(&c->opts, i));
+ int ret = bch2_opt_hook_pre_set(c, NULL, 0, i, bch2_opt_get_by_id(&c->opts, i), false);
if (ret)
return ret;
}
@@ -555,27 +571,20 @@ int bch2_opts_hooks_pre_set(struct bch_fs *c)
}
void bch2_opt_hook_post_set(struct bch_fs *c, struct bch_dev *ca, u64 inum,
- struct bch_opts *new_opts, enum bch_opt_id id)
+ enum bch_opt_id id, u64 v)
{
+ if (id == Opt_foreground_target ||
+ id == Opt_background_target ||
+ id == Opt_promote_target ||
+ id == Opt_compression ||
+ id == Opt_background_compression ||
+ id == Opt_data_checksum ||
+ id == Opt_data_replicas) {
+ bch2_set_rebalance_needs_scan(c, inum);
+ bch2_rebalance_wakeup(c);
+ }
+
switch (id) {
- case Opt_foreground_target:
- if (new_opts->foreground_target &&
- !new_opts->background_target)
- bch2_set_rebalance_needs_scan(c, inum);
- break;
- case Opt_compression:
- if (new_opts->compression &&
- !new_opts->background_compression)
- bch2_set_rebalance_needs_scan(c, inum);
- break;
- case Opt_background_target:
- if (new_opts->background_target)
- bch2_set_rebalance_needs_scan(c, inum);
- break;
- case Opt_background_compression:
- if (new_opts->background_compression)
- bch2_set_rebalance_needs_scan(c, inum);
- break;
case Opt_rebalance_enabled:
bch2_rebalance_wakeup(c);
break;
@@ -600,7 +609,7 @@ void bch2_opt_hook_post_set(struct bch_fs *c, struct bch_dev *ca, u64 inum,
* upgrades at runtime as well, but right now there's nothing
* that does that:
*/
- if (new_opts->version_upgrade == BCH_VERSION_UPGRADE_incompatible)
+ if (v == BCH_VERSION_UPGRADE_incompatible)
bch2_sb_upgrade_incompat(c);
break;
default:
diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
index 263aeb55eef9..8b38f27afea4 100644
--- a/fs/bcachefs/opts.h
+++ b/fs/bcachefs/opts.h
@@ -658,10 +658,9 @@ void bch2_opts_to_text(struct printbuf *,
struct bch_fs *, struct bch_sb *,
unsigned, unsigned, unsigned);
-int bch2_opt_hook_pre_set(struct bch_fs *, struct bch_dev *, enum bch_opt_id, u64);
+int bch2_opt_hook_pre_set(struct bch_fs *, struct bch_dev *, u64, enum bch_opt_id, u64, bool);
int bch2_opts_hooks_pre_set(struct bch_fs *);
-void bch2_opt_hook_post_set(struct bch_fs *, struct bch_dev *, u64,
- struct bch_opts *, enum bch_opt_id);
+void bch2_opt_hook_post_set(struct bch_fs *, struct bch_dev *, u64, enum bch_opt_id, u64);
int bch2_parse_one_mount_opt(struct bch_fs *, struct bch_opts *,
struct printbuf *, const char *, const char *);
diff --git a/fs/bcachefs/rebalance.c b/fs/bcachefs/rebalance.c
index 9e22ff0e2d28..f1497302332f 100644
--- a/fs/bcachefs/rebalance.c
+++ b/fs/bcachefs/rebalance.c
@@ -25,6 +25,8 @@
#include <linux/kthread.h>
#include <linux/sched/cputime.h>
+#define REBALANCE_WORK_SCAN_OFFSET (U64_MAX - 1)
+
/* bch_extent_rebalance: */
static const struct bch_extent_rebalance *bch2_bkey_ptrs_rebalance_opts(struct bkey_ptrs_c ptrs)
@@ -43,108 +45,148 @@ static const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s
return bch2_bkey_ptrs_rebalance_opts(bch2_bkey_ptrs_c(k));
}
-static inline unsigned bch2_bkey_ptrs_need_compress(struct bch_fs *c,
- struct bch_inode_opts *opts,
- struct bkey_s_c k,
- struct bkey_ptrs_c ptrs)
+void bch2_extent_rebalance_to_text(struct printbuf *out, struct bch_fs *c,
+ const struct bch_extent_rebalance *r)
{
- if (!opts->background_compression)
- return 0;
+ prt_str(out, "rebalance:");
- unsigned compression_type = bch2_compression_opt_to_type(opts->background_compression);
- const union bch_extent_entry *entry;
- struct extent_ptr_decoded p;
- unsigned ptr_bit = 1;
- unsigned rewrite_ptrs = 0;
+ prt_printf(out, " replicas=%u", r->data_replicas);
+ if (r->data_replicas_from_inode)
+ prt_str(out, " (inode)");
- bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
- if (p.crc.compression_type == BCH_COMPRESSION_TYPE_incompressible ||
- p.ptr.unwritten)
- return 0;
+ prt_str(out, " checksum=");
+ bch2_prt_csum_opt(out, r->data_checksum);
+ if (r->data_checksum_from_inode)
+ prt_str(out, " (inode)");
+
+ if (r->background_compression || r->background_compression_from_inode) {
+ prt_str(out, " background_compression=");
+ bch2_compression_opt_to_text(out, r->background_compression);
- if (!p.ptr.cached && p.crc.compression_type != compression_type)
- rewrite_ptrs |= ptr_bit;
- ptr_bit <<= 1;
+ if (r->background_compression_from_inode)
+ prt_str(out, " (inode)");
}
- return rewrite_ptrs;
-}
+ if (r->background_target || r->background_target_from_inode) {
+ prt_str(out, " background_target=");
+ if (c)
+ bch2_target_to_text(out, c, r->background_target);
+ else
+ prt_printf(out, "%u", r->background_target);
-static inline unsigned bch2_bkey_ptrs_need_move(struct bch_fs *c,
- struct bch_inode_opts *opts,
- struct bkey_ptrs_c ptrs)
-{
- if (!opts->background_target ||
- !bch2_target_accepts_data(c, BCH_DATA_user, opts->background_target))
- return 0;
+ if (r->background_target_from_inode)
+ prt_str(out, " (inode)");
+ }
- unsigned ptr_bit = 1;
- unsigned rewrite_ptrs = 0;
+ if (r->promote_target || r->promote_target_from_inode) {
+ prt_str(out, " promote_target=");
+ if (c)
+ bch2_target_to_text(out, c, r->promote_target);
+ else
+ prt_printf(out, "%u", r->promote_target);
- guard(rcu)();
- bkey_for_each_ptr(ptrs, ptr) {
- if (!ptr->cached && !bch2_dev_in_target(c, ptr->dev, opts->background_target))
- rewrite_ptrs |= ptr_bit;
- ptr_bit <<= 1;
+ if (r->promote_target_from_inode)
+ prt_str(out, " (inode)");
}
- return rewrite_ptrs;
+ if (r->erasure_code || r->erasure_code_from_inode) {
+ prt_printf(out, " ec=%u", r->erasure_code);
+ if (r->erasure_code_from_inode)
+ prt_str(out, " (inode)");
+ }
}
-static unsigned bch2_bkey_ptrs_need_rebalance(struct bch_fs *c,
- struct bch_inode_opts *opts,
- struct bkey_s_c k)
+static void bch2_bkey_needs_rebalance(struct bch_fs *c, struct bkey_s_c k,
+ struct bch_inode_opts *io_opts,
+ unsigned *move_ptrs,
+ unsigned *compress_ptrs,
+ u64 *sectors)
{
- struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
-
- if (bch2_bkey_extent_ptrs_flags(ptrs) & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
- return 0;
+ *move_ptrs = 0;
+ *compress_ptrs = 0;
+ *sectors = 0;
- return bch2_bkey_ptrs_need_compress(c, opts, k, ptrs) |
- bch2_bkey_ptrs_need_move(c, opts, ptrs);
-}
-
-u64 bch2_bkey_sectors_need_rebalance(struct bch_fs *c, struct bkey_s_c k)
-{
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
- const struct bch_extent_rebalance *opts = bch2_bkey_ptrs_rebalance_opts(ptrs);
- if (!opts)
- return 0;
+ const struct bch_extent_rebalance *rb_opts = bch2_bkey_ptrs_rebalance_opts(ptrs);
+ if (!io_opts && !rb_opts)
+ return;
if (bch2_bkey_extent_ptrs_flags(ptrs) & BIT_ULL(BCH_EXTENT_FLAG_poisoned))
- return 0;
+ return;
+
+ unsigned compression_type =
+ bch2_compression_opt_to_type(io_opts
+ ? io_opts->background_compression
+ : rb_opts->background_compression);
+ unsigned target = io_opts
+ ? io_opts->background_target
+ : rb_opts->background_target;
+ if (target && !bch2_target_accepts_data(c, BCH_DATA_user, target))
+ target = 0;
const union bch_extent_entry *entry;
struct extent_ptr_decoded p;
- u64 sectors = 0;
+ bool incompressible = false, unwritten = false;
- if (opts->background_compression) {
- unsigned compression_type = bch2_compression_opt_to_type(opts->background_compression);
+ unsigned ptr_idx = 1;
- bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
- if (p.crc.compression_type == BCH_COMPRESSION_TYPE_incompressible ||
- p.ptr.unwritten) {
- sectors = 0;
- goto incompressible;
- }
+ guard(rcu)();
+ bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
+ incompressible |= p.crc.compression_type == BCH_COMPRESSION_TYPE_incompressible;
+ unwritten |= p.ptr.unwritten;
+
+ if (!p.ptr.cached) {
+ if (p.crc.compression_type != compression_type)
+ *compress_ptrs |= ptr_idx;
- if (!p.ptr.cached && p.crc.compression_type != compression_type)
- sectors += p.crc.compressed_size;
+ if (target && !bch2_dev_in_target(c, p.ptr.dev, target))
+ *move_ptrs |= ptr_idx;
}
+
+ ptr_idx <<= 1;
}
-incompressible:
- if (opts->background_target) {
- guard(rcu)();
- bkey_for_each_ptr_decode(k.k, ptrs, p, entry)
- if (!p.ptr.cached &&
- !bch2_dev_in_target(c, p.ptr.dev, opts->background_target))
- sectors += p.crc.compressed_size;
+
+ if (unwritten)
+ *compress_ptrs = 0;
+ if (incompressible)
+ *compress_ptrs = 0;
+
+ unsigned rb_ptrs = *move_ptrs | *compress_ptrs;
+
+ if (!rb_ptrs)
+ return;
+
+ ptr_idx = 1;
+ bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
+ if (rb_ptrs & ptr_idx)
+ *sectors += p.crc.compressed_size;
+ ptr_idx <<= 1;
}
+}
+
+u64 bch2_bkey_sectors_need_rebalance(struct bch_fs *c, struct bkey_s_c k)
+{
+ unsigned move_ptrs = 0;
+ unsigned compress_ptrs = 0;
+ u64 sectors = 0;
+ bch2_bkey_needs_rebalance(c, k, NULL, &move_ptrs, &compress_ptrs, &sectors);
return sectors;
}
+static unsigned bch2_bkey_ptrs_need_rebalance(struct bch_fs *c,
+ struct bch_inode_opts *opts,
+ struct bkey_s_c k)
+{
+ unsigned move_ptrs = 0;
+ unsigned compress_ptrs = 0;
+ u64 sectors = 0;
+
+ bch2_bkey_needs_rebalance(c, k, opts, &move_ptrs, &compress_ptrs, &sectors);
+ return move_ptrs|compress_ptrs;
+}
+
static inline bool bkey_should_have_rb_opts(struct bch_fs *c,
struct bch_inode_opts *opts,
struct bkey_s_c k)
@@ -179,6 +221,35 @@ int bch2_bkey_set_needs_rebalance(struct bch_fs *c, struct bch_inode_opts *opts,
return 0;
}
+static int have_rebalance_scan_cookie(struct btree_trans *trans, u64 inum)
+{
+ /*
+ * If opts need to be propagated to the extent, a scan cookie should be
+ * present:
+ */
+ CLASS(btree_iter, iter)(trans, BTREE_ID_rebalance_work,
+ SPOS(inum, REBALANCE_WORK_SCAN_OFFSET, U32_MAX),
+ BTREE_ITER_intent);
+ struct bkey_s_c k = bch2_btree_iter_peek_slot(&iter);
+ int ret = bkey_err(k);
+ if (ret)
+ return ret;
+
+ if (k.k->type == KEY_TYPE_cookie)
+ return 1;
+
+ if (!inum)
+ return 0;
+
+ bch2_btree_iter_set_pos(&iter, SPOS(0, REBALANCE_WORK_SCAN_OFFSET, U32_MAX));
+ k = bch2_btree_iter_peek_slot(&iter);
+ ret = bkey_err(k);
+ if (ret)
+ return ret;
+
+ return k.k->type == KEY_TYPE_cookie;
+}
+
static int bch2_get_update_rebalance_opts(struct btree_trans *trans,
struct bch_inode_opts *io_opts,
struct btree_iter *iter,
@@ -186,6 +257,7 @@ static int bch2_get_update_rebalance_opts(struct btree_trans *trans,
enum set_needs_rebalance_ctx ctx)
{
struct bch_fs *c = trans->c;
+ int ret = 0;
BUG_ON(iter->flags & BTREE_ITER_is_extents);
BUG_ON(iter->flags & BTREE_ITER_filter_snapshots);
@@ -216,13 +288,61 @@ static int bch2_get_update_rebalance_opts(struct btree_trans *trans,
struct bch_extent_rebalance new = io_opts_to_rebalance_opts(c, io_opts);
- if (bkey_should_have_rb_opts(c, io_opts, k)
+ bool should_have_rb_opts = bkey_should_have_rb_opts(c, io_opts, k);
+
+ if (should_have_rb_opts
? old && !memcmp(old, &new, sizeof(new))
: !old)
return 0;
+ if (k.k->type != KEY_TYPE_reflink_v) {
+ if (old && !should_have_rb_opts) {
+ CLASS(printbuf, buf)();
+
+ prt_printf(&buf, "extent with unneeded rebalance opts:\n");
+ bch2_bkey_val_to_text(&buf, c, k);
+
+ fsck_err(trans, extent_io_opts_not_set, "%s", buf.buf);
+ } else {
+ ret = have_rebalance_scan_cookie(trans, k.k->p.inode);
+ if (ret < 0)
+ return ret;
+
+ if (!ret) {
+ CLASS(printbuf, buf)();
+
+ prt_printf(&buf, "extent with incorrect/missing rebalance opts:\n");
+ bch2_bkey_val_to_text(&buf, c, k);
+ const struct bch_extent_rebalance _old = {};
+ if (!old)
+ old = &_old;
+
+#define x(_name) \
+ if (old->_name != new._name) \
+ prt_printf(&buf, "\n" #_name " %u != %u", \
+ old->_name, new._name); \
+ if (old->_name##_from_inode != new._name##_from_inode) \
+ prt_printf(&buf, "\n" #_name "_from_inode %u != %u", \
+ old->_name##_from_inode, new._name##_from_inode);
+ BCH_REBALANCE_OPTS()
+#undef x
+
+ if (old->unused != new.unused)
+ prt_printf(&buf, "\nunused %u != %u", old->unused, new.unused);
+
+ if (old->type != new.type)
+ prt_printf(&buf, "\ntype %u != %u", old->type, new.type);
+
+ prt_newline(&buf);
+ bch2_extent_rebalance_to_text(&buf, c, &new);
+
+ fsck_err(trans, extent_io_opts_not_set, "%s", buf.buf);
+ }
+ }
+ }
+
struct bkey_i *n = bch2_trans_kmalloc(trans, bkey_bytes(k.k) + 8);
- int ret = PTR_ERR_OR_ZERO(n);
+ ret = PTR_ERR_OR_ZERO(n);
if (ret)
return ret;
@@ -230,10 +350,12 @@ static int bch2_get_update_rebalance_opts(struct btree_trans *trans,
/* On successfull transaction commit, @k was invalidated: */
- return bch2_bkey_set_needs_rebalance(c, io_opts, n, ctx, 0) ?:
+ ret = bch2_bkey_set_needs_rebalance(c, io_opts, n, ctx, 0) ?:
bch2_trans_update(trans, iter, n, BTREE_UPDATE_internal_snapshot_node) ?:
bch2_trans_commit(trans, NULL, NULL, 0) ?:
bch_err_throw(c, transaction_restart_commit);
+fsck_err:
+ return ret;
}
static struct bch_inode_opts *bch2_extent_get_io_opts(struct btree_trans *trans,
@@ -336,8 +458,6 @@ int bch2_extent_get_io_opts_one(struct btree_trans *trans,
ctx);
}
-#define REBALANCE_WORK_SCAN_OFFSET (U64_MAX - 1)
-
static const char * const bch2_rebalance_state_strs[] = {
#define x(t) #t,
BCH_REBALANCE_STATES()
@@ -518,23 +638,25 @@ static struct bkey_s_c next_rebalance_extent(struct btree_trans *trans,
bch2_bkey_val_to_text(&buf, c, k);
prt_newline(&buf);
- struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
+ unsigned move_ptrs = 0;
+ unsigned compress_ptrs = 0;
+ u64 sectors = 0;
- unsigned p = bch2_bkey_ptrs_need_compress(c, opts, k, ptrs);
- if (p) {
- prt_str(&buf, "compression=");
- bch2_compression_opt_to_text(&buf, opts->background_compression);
+ bch2_bkey_needs_rebalance(c, k, opts, &move_ptrs, &compress_ptrs, &sectors);
+
+ if (move_ptrs) {
+ prt_str(&buf, "move=");
+ bch2_target_to_text(&buf, c, opts->background_target);
prt_str(&buf, " ");
- bch2_prt_u64_base2(&buf, p);
+ bch2_prt_u64_base2(&buf, move_ptrs);
prt_newline(&buf);
}
- p = bch2_bkey_ptrs_need_move(c, opts, ptrs);
- if (p) {
- prt_str(&buf, "move=");
- bch2_target_to_text(&buf, c, opts->background_target);
+ if (compress_ptrs) {
+ prt_str(&buf, "compression=");
+ bch2_compression_opt_to_text(&buf, opts->background_compression);
prt_str(&buf, " ");
- bch2_prt_u64_base2(&buf, p);
+ bch2_prt_u64_base2(&buf, compress_ptrs);
prt_newline(&buf);
}
@@ -649,6 +771,8 @@ static int do_rebalance_scan(struct moving_context *ctxt,
BTREE_ITER_prefetch, k, ({
ctxt->stats->pos = BBPOS(iter.btree_id, iter.pos);
+ atomic64_add(k.k->size, &r->scan_stats.sectors_seen);
+
struct bch_inode_opts *opts = bch2_extent_get_apply_io_opts(trans,
snapshot_io_opts, iter.pos, &iter, k,
SET_NEEDS_REBALANCE_opt_change);
@@ -658,10 +782,31 @@ static int do_rebalance_scan(struct moving_context *ctxt,
REFLINK_P_MAY_UPDATE_OPTIONS(bkey_s_c_to_reflink_p(k).v)
? do_rebalance_scan_indirect(trans, bkey_s_c_to_reflink_p(k), opts)
: 0);
- })) ?:
- commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
- bch2_clear_rebalance_needs_scan(trans, inum, cookie));
+ }));
+ if (ret)
+ goto out;
+
+ if (!inum) {
+ ret = for_each_btree_key_max(trans, iter, BTREE_ID_reflink,
+ POS_MIN, POS_MAX,
+ BTREE_ITER_all_snapshots|
+ BTREE_ITER_prefetch, k, ({
+ ctxt->stats->pos = BBPOS(iter.btree_id, iter.pos);
+
+ atomic64_add(k.k->size, &r->scan_stats.sectors_seen);
+
+ struct bch_inode_opts *opts = bch2_extent_get_apply_io_opts(trans,
+ snapshot_io_opts, iter.pos, &iter, k,
+ SET_NEEDS_REBALANCE_opt_change);
+ PTR_ERR_OR_ZERO(opts);
+ }));
+ if (ret)
+ goto out;
+ }
+ ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+ bch2_clear_rebalance_needs_scan(trans, inum, cookie));
+out:
*sectors_scanned += atomic64_read(&r->scan_stats.sectors_seen);
/*
* Ensure that the rebalance_work entries we created are seen by the
diff --git a/fs/bcachefs/rebalance.h b/fs/bcachefs/rebalance.h
index fd33e7aa2ecb..7a4e3a56d5a2 100644
--- a/fs/bcachefs/rebalance.h
+++ b/fs/bcachefs/rebalance.h
@@ -26,6 +26,9 @@ static inline struct bch_extent_rebalance io_opts_to_rebalance_opts(struct bch_f
return r;
};
+void bch2_extent_rebalance_to_text(struct printbuf *, struct bch_fs *,
+ const struct bch_extent_rebalance *);
+
u64 bch2_bkey_sectors_need_rebalance(struct bch_fs *, struct bkey_s_c);
enum set_needs_rebalance_ctx {
diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h
index aa0ea1ec9f10..cac8236317b2 100644
--- a/fs/bcachefs/sb-errors_format.h
+++ b/fs/bcachefs/sb-errors_format.h
@@ -337,7 +337,9 @@ enum bch_fsck_flags {
x(dirent_stray_data_after_cf_name, 305, 0) \
x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \
x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \
- x(MAX, 326, 0)
+ x(extent_io_opts_not_set, 326, FSCK_AUTOFIX) \
+ x(extent_io_opts_unneeded, 327, FSCK_AUTOFIX) \
+ x(MAX, 328, 0)
enum bch_sb_error_id {
#define x(t, n, ...) BCH_FSCK_ERR_##t = n,
diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c
index 6b071dcc062b..ef6312c50f88 100644
--- a/fs/bcachefs/sysfs.c
+++ b/fs/bcachefs/sysfs.c
@@ -784,7 +784,7 @@ static ssize_t sysfs_opt_store(struct bch_fs *c,
u64 v;
ret = bch2_opt_parse(c, opt, strim(tmp), &v, NULL) ?:
- bch2_opt_hook_pre_set(c, ca, id, v);
+ bch2_opt_hook_pre_set(c, ca, 0, id, v, true);
kfree(tmp);
if (ret < 0)
@@ -807,7 +807,7 @@ static ssize_t sysfs_opt_store(struct bch_fs *c,
bch2_opt_set_by_id(&c->opts, id, v);
if (changed)
- bch2_opt_hook_post_set(c, ca, 0, &c->opts, id);
+ bch2_opt_hook_post_set(c, ca, 0, id, v);
ret = size;
err:
diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c
index de72a735a49d..2b8d0502db1e 100644
--- a/fs/bcachefs/xattr.c
+++ b/fs/bcachefs/xattr.c
@@ -535,10 +535,9 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
return -EINVAL;
s.id = inode_opt_id;
+ u64 v = 0;
if (value) {
- u64 v = 0;
-
buf = kmalloc(size + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -551,7 +550,7 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
if (ret < 0)
goto err;
- ret = bch2_opt_hook_pre_set(c, NULL, opt_id, v);
+ ret = bch2_opt_hook_pre_set(c, NULL, inode->ei_inode.bi_inum, opt_id, v, true);
if (ret < 0)
goto err;
@@ -590,10 +589,9 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
}
ret = bch2_write_inode(c, inode, inode_opt_set_fn, &s, 0);
-
- if (!ret)
- atomic_inc(&c->opt_change_cookie);
}
+
+ bch2_opt_hook_post_set(c, NULL, inode->ei_inode.bi_inum, opt_id, v);
err:
return bch2_err_class(ret);
}