summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-02-07 08:08:24 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-02-08 15:33:42 -0500
commit6976570d670e4053f026d380cfe5397a0a8ed139 (patch)
tree29abbf0f904cf1a12e7299b270cf59f1e22c3be3
parenta9f5937a9764003f3c88ccff4f92da259f025674 (diff)
Update bcachefs sources to d5e561b3cc bcachefs: BCH_DATA ioctl
-rw-r--r--.bcachefs_revision2
-rw-r--r--include/linux/bio.h2
-rw-r--r--include/linux/completion.h2
-rw-r--r--include/linux/random.h5
-rw-r--r--libbcachefs.c5
-rw-r--r--libbcachefs/bcachefs.h2
-rw-r--r--libbcachefs/bcachefs_format.h40
-rw-r--r--libbcachefs/bcachefs_ioctl.h44
-rw-r--r--libbcachefs/chardev.c136
-rw-r--r--libbcachefs/extents.c28
-rw-r--r--libbcachefs/extents.h4
-rw-r--r--libbcachefs/journal.c108
-rw-r--r--libbcachefs/journal.h2
-rw-r--r--libbcachefs/migrate.c3
-rw-r--r--libbcachefs/move.c221
-rw-r--r--libbcachefs/move.h8
-rw-r--r--libbcachefs/movinggc.c2
-rw-r--r--libbcachefs/super-io.c134
-rw-r--r--libbcachefs/super-io.h62
-rw-r--r--libbcachefs/super.c53
-rw-r--r--libbcachefs/super.h5
-rw-r--r--libbcachefs/super_types.h12
-rw-r--r--libbcachefs/tier.c2
-rw-r--r--linux/bio.c9
24 files changed, 717 insertions, 174 deletions
diff --git a/.bcachefs_revision b/.bcachefs_revision
index f35d38b..274236e 100644
--- a/.bcachefs_revision
+++ b/.bcachefs_revision
@@ -1 +1 @@
-496cbe9474173ec41bf221dc8ab1f5d70a128c3b
+d5e561b3cc023dd247d2b3d08b680709ec21b477
diff --git a/include/linux/bio.h b/include/linux/bio.h
index dcaffed..1bd21ee 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -257,6 +257,8 @@ static inline void bio_flush_dcache_pages(struct bio *bi)
extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
struct bio *src, struct bvec_iter *src_iter);
extern void bio_copy_data(struct bio *dst, struct bio *src);
+
+void bio_free_pages(struct bio *bio);
extern int bio_alloc_pages(struct bio *bio, gfp_t gfp);
void zero_fill_bio_iter(struct bio *bio, struct bvec_iter iter);
diff --git a/include/linux/completion.h b/include/linux/completion.h
index 1808d21..d11a8dd 100644
--- a/include/linux/completion.h
+++ b/include/linux/completion.h
@@ -37,4 +37,6 @@ static inline void reinit_completion(struct completion *x)
void complete(struct completion *);
void wait_for_completion(struct completion *);
+#define wait_for_completion_interruptible(x) (wait_for_completion(x), 0)
+
#endif
diff --git a/include/linux/random.h b/include/linux/random.h
index 243c060..90fe574 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -29,6 +29,11 @@ static inline void get_random_bytes(void *buf, int nbytes)
BUG_ON(getrandom(buf, nbytes, 0) != nbytes);
}
+static inline void prandom_bytes(void *buf, int nbytes)
+{
+ return get_random_bytes(buf, nbytes);
+}
+
#define get_random_type(type) \
static inline type get_random_##type(void) \
{ \
diff --git a/libbcachefs.c b/libbcachefs.c
index 3632e30..238cca9 100644
--- a/libbcachefs.c
+++ b/libbcachefs.c
@@ -459,6 +459,11 @@ static void bch2_sb_print_quota(struct bch_sb *sb, struct bch_sb_field *f,
{
}
+static void bch2_sb_print_disk_groups(struct bch_sb *sb, struct bch_sb_field *f,
+ enum units units)
+{
+}
+
typedef void (*sb_field_print_fn)(struct bch_sb *, struct bch_sb_field *, enum units);
struct bch_sb_field_ops {
diff --git a/libbcachefs/bcachefs.h b/libbcachefs/bcachefs.h
index 298f26d..cb9906c 100644
--- a/libbcachefs/bcachefs.h
+++ b/libbcachefs/bcachefs.h
@@ -496,6 +496,8 @@ struct bch_fs {
struct bch_replicas_cpu __rcu *replicas_gc;
struct mutex replicas_gc_lock;
+ struct bch_disk_groups_cpu __rcu *disk_groups;
+
struct bch_opts opts;
/* Updated by bch2_sb_update():*/
diff --git a/libbcachefs/bcachefs_format.h b/libbcachefs/bcachefs_format.h
index cb9e450..85f728f 100644
--- a/libbcachefs/bcachefs_format.h
+++ b/libbcachefs/bcachefs_format.h
@@ -782,7 +782,8 @@ struct bch_sb_field {
x(members, 1) \
x(crypt, 2) \
x(replicas, 3) \
- x(quota, 4)
+ x(quota, 4) \
+ x(disk_groups, 5)
enum bch_sb_field_type {
#define x(f, nr) BCH_SB_FIELD_##f = nr,
@@ -815,8 +816,9 @@ LE64_BITMASK(BCH_MEMBER_STATE, struct bch_member, flags[0], 0, 4)
LE64_BITMASK(BCH_MEMBER_TIER, struct bch_member, flags[0], 4, 8)
/* 8-10 unused, was HAS_(META)DATA */
LE64_BITMASK(BCH_MEMBER_REPLACEMENT, struct bch_member, flags[0], 10, 14)
-LE64_BITMASK(BCH_MEMBER_DISCARD, struct bch_member, flags[0], 14, 15);
-LE64_BITMASK(BCH_MEMBER_DATA_ALLOWED, struct bch_member, flags[0], 15, 20);
+LE64_BITMASK(BCH_MEMBER_DISCARD, struct bch_member, flags[0], 14, 15)
+LE64_BITMASK(BCH_MEMBER_DATA_ALLOWED, struct bch_member, flags[0], 15, 20)
+LE64_BITMASK(BCH_MEMBER_GROUP, struct bch_member, flags[0], 20, 28)
#if 0
LE64_BITMASK(BCH_MEMBER_NR_READ_ERRORS, struct bch_member, flags[1], 0, 20);
@@ -933,6 +935,23 @@ struct bch_sb_field_quota {
struct bch_sb_quota_type q[QTYP_NR];
} __attribute__((packed, aligned(8)));
+/* BCH_SB_FIELD_disk_groups: */
+
+#define BCH_SB_LABEL_SIZE 32
+
+struct bch_disk_group {
+ __u8 label[BCH_SB_LABEL_SIZE];
+ __le64 flags[2];
+};
+
+LE64_BITMASK(BCH_GROUP_DELETED, struct bch_disk_group, flags[0], 0, 1)
+LE64_BITMASK(BCH_GROUP_DATA_ALLOWED, struct bch_disk_group, flags[0], 1, 6)
+
+struct bch_sb_field_disk_groups {
+ struct bch_sb_field field;
+ struct bch_disk_group entries[0];
+};
+
/* Superblock: */
/*
@@ -947,7 +966,6 @@ struct bch_sb_field_quota {
#define BCH_SB_VERSION_MAX 9
#define BCH_SB_SECTOR 8
-#define BCH_SB_LABEL_SIZE 32
#define BCH_SB_MEMBERS_MAX 64 /* XXX kill */
struct bch_sb_layout {
@@ -1069,20 +1087,6 @@ enum bch_sb_features {
#define BCH_REPLICAS_MAX 4U
-#if 0
-#define BCH_ERROR_ACTIONS() \
- x(BCH_ON_ERROR_CONTINUE, 0, "continue") \
- x(BCH_ON_ERROR_RO, 1, "remount-ro") \
- x(BCH_ON_ERROR_PANIC, 2, "panic") \
- x(BCH_NR_ERROR_ACTIONS, 3, NULL)
-
-enum bch_error_actions {
-#define x(_opt, _nr, _str) _opt = _nr,
- BCH_ERROR_ACTIONS()
-#undef x
-};
-#endif
-
enum bch_error_actions {
BCH_ON_ERROR_CONTINUE = 0,
BCH_ON_ERROR_RO = 1,
diff --git a/libbcachefs/bcachefs_ioctl.h b/libbcachefs/bcachefs_ioctl.h
index aa2a205..6578847 100644
--- a/libbcachefs/bcachefs_ioctl.h
+++ b/libbcachefs/bcachefs_ioctl.h
@@ -46,7 +46,6 @@ struct bch_ioctl_incremental {
#define BCH_IOCTL_DISK_ONLINE _IOW(0xbc, 6, struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_OFFLINE _IOW(0xbc, 7, struct bch_ioctl_disk)
#define BCH_IOCTL_DISK_SET_STATE _IOW(0xbc, 8, struct bch_ioctl_disk_set_state)
-#define BCH_IOCTL_DISK_EVACUATE _IOW(0xbc, 9, struct bch_ioctl_disk)
#define BCH_IOCTL_DATA _IOW(0xbc, 10, struct bch_ioctl_data)
#define BCH_IOCTL_USAGE _IOWR(0xbc, 11, struct bch_ioctl_usage)
#define BCH_IOCTL_READ_SUPER _IOW(0xbc, 12, struct bch_ioctl_read_super)
@@ -75,30 +74,37 @@ struct bch_ioctl_disk_set_state {
__u64 dev;
};
-#define BCH_REWRITE_INCREASE_REPLICAS (1 << 0)
-#define BCH_REWRITE_DECREASE_REPLICAS (1 << 1)
-
-#define BCH_REWRITE_RECOMPRESS (1 << 0)
-#define BCH_REWRITE_DECREASE_REPLICAS (1 << 1)
-
enum bch_data_ops {
- BCH_DATA_SCRUB,
-};
-
-struct bch_data_op {
- __u8 type;
+ BCH_DATA_OP_SCRUB = 0,
+ BCH_DATA_OP_REREPLICATE = 1,
+ BCH_DATA_OP_MIGRATE = 2,
+ BCH_DATA_OP_NR = 3,
};
struct bch_ioctl_data {
+ __u32 op;
__u32 flags;
- __u32 pad;
-
- __u64 start_inode;
- __u64 start_offset;
- __u64 end_inode;
- __u64 end_offset;
-};
+ struct bpos start;
+ struct bpos end;
+
+ union {
+ struct {
+ __u32 dev;
+ __u32 pad;
+ } migrate;
+ };
+} __attribute__((packed, aligned(8)));
+
+struct bch_ioctl_data_progress {
+ __u8 data_type;
+ __u8 btree_id;
+ __u8 pad[2];
+ struct bpos pos;
+
+ __u64 sectors_done;
+ __u64 sectors_total;
+} __attribute__((packed, aligned(8)));
struct bch_ioctl_dev_usage {
__u8 state;
diff --git a/libbcachefs/chardev.c b/libbcachefs/chardev.c
index 1498832..5ff90cc 100644
--- a/libbcachefs/chardev.c
+++ b/libbcachefs/chardev.c
@@ -1,20 +1,25 @@
#ifndef NO_BCACHEFS_CHARDEV
#include "bcachefs.h"
+#include "alloc.h"
#include "bcachefs_ioctl.h"
#include "buckets.h"
#include "chardev.h"
+#include "move.h"
#include "super.h"
#include "super-io.h"
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/major.h>
+#include <linux/anon_inodes.h>
#include <linux/cdev.h>
#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/fs.h>
#include <linux/ioctl.h>
-#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/major.h>
+#include <linux/sched/task.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
/* returns with ref on ca->ref */
static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
@@ -266,23 +271,108 @@ static long bch2_ioctl_disk_set_state(struct bch_fs *c,
return ret;
}
-static long bch2_ioctl_disk_evacuate(struct bch_fs *c,
- struct bch_ioctl_disk arg)
+struct bch_data_ctx {
+ struct bch_fs *c;
+ struct bch_ioctl_data arg;
+ struct bch_move_stats stats;
+
+ int ret;
+
+ struct task_struct *thread;
+};
+
+static int bch2_data_thread(void *arg)
{
- struct bch_dev *ca;
- int ret;
+ struct bch_data_ctx *ctx = arg;
- if ((arg.flags & ~BCH_BY_INDEX) ||
- arg.pad)
+ ctx->ret = bch2_data_job(ctx->c, &ctx->stats, ctx->arg);
+
+ ctx->stats.data_type = U8_MAX;
+ return 0;
+}
+
+static int bch2_data_job_release(struct inode *inode, struct file *file)
+{
+ struct bch_data_ctx *ctx = file->private_data;
+
+ kthread_stop(ctx->thread);
+ put_task_struct(ctx->thread);
+ kfree(ctx);
+ return 0;
+}
+
+static ssize_t bch2_data_job_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct bch_data_ctx *ctx = file->private_data;
+ struct bch_fs *c = ctx->c;
+ struct bch_ioctl_data_progress p = {
+ .data_type = ctx->stats.data_type,
+ .btree_id = ctx->stats.iter.btree_id,
+ .pos = ctx->stats.iter.pos,
+ .sectors_done = atomic64_read(&ctx->stats.sectors_seen),
+ .sectors_total = bch2_fs_sectors_used(c, bch2_fs_usage_read(c)),
+ };
+
+ if (len != sizeof(p))
return -EINVAL;
- ca = bch2_device_lookup(c, arg.dev, arg.flags);
- if (IS_ERR(ca))
- return PTR_ERR(ca);
+ return copy_to_user(buf, &p, sizeof(p)) ?: sizeof(p);
+}
- ret = bch2_dev_evacuate(c, ca);
+static const struct file_operations bcachefs_data_ops = {
+ .release = bch2_data_job_release,
+ .read = bch2_data_job_read,
+ .llseek = no_llseek,
+};
- percpu_ref_put(&ca->ref);
+static long bch2_ioctl_data(struct bch_fs *c,
+ struct bch_ioctl_data arg)
+{
+ struct bch_data_ctx *ctx = NULL;
+ struct file *file = NULL;
+ unsigned flags = O_RDONLY|O_CLOEXEC|O_NONBLOCK;
+ int ret, fd = -1;
+
+ if (arg.op >= BCH_DATA_OP_NR || arg.flags)
+ return -EINVAL;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->c = c;
+ ctx->arg = arg;
+
+ ctx->thread = kthread_create(bch2_data_thread, ctx, "[bcachefs]");
+ if (IS_ERR(ctx->thread)) {
+ ret = PTR_ERR(ctx->thread);
+ goto err;
+ }
+
+ ret = get_unused_fd_flags(flags);
+ if (ret < 0)
+ goto err;
+ fd = ret;
+
+ file = anon_inode_getfile("[bcachefs]", &bcachefs_data_ops, ctx, flags);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err;
+ }
+
+ fd_install(fd, file);
+
+ get_task_struct(ctx->thread);
+ wake_up_process(ctx->thread);
+
+ return fd;
+err:
+ if (fd >= 0)
+ put_unused_fd(fd);
+ if (!IS_ERR_OR_NULL(ctx->thread))
+ kthread_stop(ctx->thread);
+ kfree(ctx);
return ret;
}
@@ -474,8 +564,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
BCH_IOCTL(disk_offline, struct bch_ioctl_disk);
case BCH_IOCTL_DISK_SET_STATE:
BCH_IOCTL(disk_set_state, struct bch_ioctl_disk_set_state);
- case BCH_IOCTL_DISK_EVACUATE:
- BCH_IOCTL(disk_evacuate, struct bch_ioctl_disk);
+ case BCH_IOCTL_DATA:
+ BCH_IOCTL(data, struct bch_ioctl_data);
case BCH_IOCTL_READ_SUPER:
BCH_IOCTL(read_super, struct bch_ioctl_read_super);
case BCH_IOCTL_DISK_GET_IDX:
@@ -488,9 +578,12 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
}
}
+static DEFINE_IDR(bch_chardev_minor);
+
static long bch2_chardev_ioctl(struct file *filp, unsigned cmd, unsigned long v)
{
- struct bch_fs *c = filp->private_data;
+ unsigned minor = iminor(file_inode(filp));
+ struct bch_fs *c = minor < U8_MAX ? idr_find(&bch_chardev_minor, minor) : NULL;
void __user *arg = (void __user *) v;
return c
@@ -507,7 +600,6 @@ static const struct file_operations bch_chardev_fops = {
static int bch_chardev_major;
static struct class *bch_chardev_class;
static struct device *bch_chardev;
-static DEFINE_IDR(bch_chardev_minor);
void bch2_fs_chardev_exit(struct bch_fs *c)
{
@@ -524,7 +616,7 @@ int bch2_fs_chardev_init(struct bch_fs *c)
return c->minor;
c->chardev = device_create(bch_chardev_class, NULL,
- MKDEV(bch_chardev_major, c->minor), NULL,
+ MKDEV(bch_chardev_major, c->minor), c,
"bcachefs%u-ctl", c->minor);
if (IS_ERR(c->chardev))
return PTR_ERR(c->chardev);
@@ -536,7 +628,7 @@ void bch2_chardev_exit(void)
{
if (!IS_ERR_OR_NULL(bch_chardev_class))
device_destroy(bch_chardev_class,
- MKDEV(bch_chardev_major, 255));
+ MKDEV(bch_chardev_major, U8_MAX));
if (!IS_ERR_OR_NULL(bch_chardev_class))
class_destroy(bch_chardev_class);
if (bch_chardev_major > 0)
@@ -554,7 +646,7 @@ int __init bch2_chardev_init(void)
return PTR_ERR(bch_chardev_class);
bch_chardev = device_create(bch_chardev_class, NULL,
- MKDEV(bch_chardev_major, 255),
+ MKDEV(bch_chardev_major, U8_MAX),
NULL, "bcachefs-ctl");
if (IS_ERR(bch_chardev))
return PTR_ERR(bch_chardev);
diff --git a/libbcachefs/extents.c b/libbcachefs/extents.c
index c246916..f5dccfa 100644
--- a/libbcachefs/extents.c
+++ b/libbcachefs/extents.c
@@ -139,6 +139,34 @@ bool bch2_extent_drop_device(struct bkey_s_extent e, unsigned dev)
return dropped;
}
+const struct bch_extent_ptr *
+bch2_extent_has_group(struct bch_fs *c, struct bkey_s_c_extent e, unsigned group)
+{
+ const struct bch_extent_ptr *ptr;
+
+ extent_for_each_ptr(e, ptr) {
+ struct bch_dev *ca = c->devs[ptr->dev];
+
+ if (ca->mi.group &&
+ ca->mi.group == group)
+ return ptr;
+ }
+
+ return NULL;
+}
+
+const struct bch_extent_ptr *
+bch2_extent_has_target(struct bch_fs *c, struct bkey_s_c_extent e, unsigned target)
+{
+ const struct bch_extent_ptr *ptr;
+
+ extent_for_each_ptr(e, ptr)
+ if (dev_in_target(c->devs[ptr->dev], target))
+ return ptr;
+
+ return NULL;
+}
+
unsigned bch2_extent_nr_ptrs(struct bkey_s_c_extent e)
{
const struct bch_extent_ptr *ptr;
diff --git a/libbcachefs/extents.h b/libbcachefs/extents.h
index eda3438..e8f54f2 100644
--- a/libbcachefs/extents.h
+++ b/libbcachefs/extents.h
@@ -43,6 +43,10 @@ void bch2_extent_mark_replicas_cached(struct bch_fs *, struct bkey_s_extent);
const struct bch_extent_ptr *
bch2_extent_has_device(struct bkey_s_c_extent, unsigned);
bool bch2_extent_drop_device(struct bkey_s_extent, unsigned);
+const struct bch_extent_ptr *
+bch2_extent_has_group(struct bch_fs *, struct bkey_s_c_extent, unsigned);
+const struct bch_extent_ptr *
+bch2_extent_has_target(struct bch_fs *, struct bkey_s_c_extent, unsigned);
unsigned bch2_extent_nr_ptrs(struct bkey_s_c_extent);
unsigned bch2_extent_nr_dirty_ptrs(struct bkey_s_c);
diff --git a/libbcachefs/journal.c b/libbcachefs/journal.c
index 1870534..a1e4562 100644
--- a/libbcachefs/journal.c
+++ b/libbcachefs/journal.c
@@ -54,6 +54,13 @@ static inline u64 journal_last_seq(struct journal *j)
return j->pin.front;
}
+static inline u64 journal_cur_seq(struct journal *j)
+{
+ BUG_ON(j->pin.back - 1 != atomic64_read(&j->seq));
+
+ return j->pin.back - 1;
+}
+
static inline u64 journal_pin_seq(struct journal *j,
struct journal_entry_pin_list *pin_list)
{
@@ -264,7 +271,9 @@ int bch2_journal_seq_should_ignore(struct bch_fs *c, u64 seq, struct btree *b)
if (!seq)
return 0;
- journal_seq = atomic64_read(&j->seq);
+ spin_lock(&j->lock);
+ journal_seq = journal_cur_seq(j);
+ spin_unlock(&j->lock);
/* Interier updates aren't journalled: */
BUG_ON(b->level);
@@ -989,6 +998,7 @@ int bch2_journal_read(struct bch_fs *c, struct list_head *list)
u64 cur_seq, end_seq, seq;
unsigned iter, keys = 0, entries = 0;
size_t nr;
+ bool degraded = false;
int ret = 0;
closure_init_stack(&jlist.cl);
@@ -996,12 +1006,19 @@ int bch2_journal_read(struct bch_fs *c, struct list_head *list)
jlist.head = list;
jlist.ret = 0;
- for_each_readable_member(ca, c, iter) {
- percpu_ref_get(&ca->io_ref);
- closure_call(&ca->journal.read,
- bch2_journal_read_device,
- system_unbound_wq,
- &jlist.cl);
+ for_each_member_device(ca, c, iter) {
+ if (!(bch2_dev_has_data(c, ca) & (1 << BCH_DATA_JOURNAL)))
+ continue;
+
+ if ((ca->mi.state == BCH_MEMBER_STATE_RW ||
+ ca->mi.state == BCH_MEMBER_STATE_RO) &&
+ percpu_ref_tryget(&ca->io_ref))
+ closure_call(&ca->journal.read,
+ bch2_journal_read_device,
+ system_unbound_wq,
+ &jlist.cl);
+ else
+ degraded = true;
}
closure_sync(&jlist.cl);
@@ -1022,11 +1039,17 @@ int bch2_journal_read(struct bch_fs *c, struct list_head *list)
if (ret)
goto fsck_err;
- if (test_bit(BCH_FS_REBUILD_REPLICAS, &c->flags) ||
- fsck_err_on(!bch2_sb_has_replicas(c, BCH_DATA_JOURNAL,
- i->devs), c,
- "superblock not marked as containing replicas (type %u)",
- BCH_DATA_JOURNAL)) {
+ /*
+ * If we're mounting in degraded mode - if we didn't read all
+ * the devices - this is wrong:
+ */
+
+ if (!degraded &&
+ (test_bit(BCH_FS_REBUILD_REPLICAS, &c->flags) ||
+ fsck_err_on(!bch2_sb_has_replicas(c, BCH_DATA_JOURNAL,
+ i->devs), c,
+ "superblock not marked as containing replicas (type %u)",
+ BCH_DATA_JOURNAL))) {
ret = bch2_check_mark_super(c, BCH_DATA_JOURNAL,
i->devs);
if (ret)
@@ -1111,7 +1134,7 @@ int bch2_journal_read(struct bch_fs *c, struct list_head *list)
}
bch_info(c, "journal read done, %i keys in %i entries, seq %llu",
- keys, entries, (u64) atomic64_read(&j->seq));
+ keys, entries, journal_cur_seq(j));
fsck_err:
return ret;
}
@@ -1174,9 +1197,6 @@ static void journal_pin_new_entry(struct journal *j, int count)
atomic64_inc(&j->seq);
p = fifo_push_ref(&j->pin);
- EBUG_ON(journal_seq_pin(j, atomic64_read(&j->seq)) !=
- &fifo_peek_back(&j->pin));
-
INIT_LIST_HEAD(&p->list);
INIT_LIST_HEAD(&p->flushed);
atomic_set(&p->count, count);
@@ -1190,7 +1210,7 @@ static void bch2_journal_buf_init(struct journal *j)
memset(buf->has_inode, 0, sizeof(buf->has_inode));
memset(buf->data, 0, sizeof(*buf->data));
- buf->data->seq = cpu_to_le64(atomic64_read(&j->seq));
+ buf->data->seq = cpu_to_le64(journal_cur_seq(j));
buf->data->u64s = 0;
}
@@ -1472,7 +1492,7 @@ void bch2_journal_start(struct bch_fs *c)
set_bit(JOURNAL_STARTED, &j->flags);
- while (atomic64_read(&j->seq) < new_seq)
+ while (journal_cur_seq(j) < new_seq)
journal_pin_new_entry(j, 0);
/*
@@ -2015,9 +2035,11 @@ static void journal_reclaim_work(struct work_struct *work)
mutex_unlock(&j->reclaim_lock);
/* Also flush if the pin fifo is more than half full */
+ spin_lock(&j->lock);
seq_to_flush = max_t(s64, seq_to_flush,
- (s64) atomic64_read(&j->seq) -
+ (s64) journal_cur_seq(j) -
(j->pin.size >> 1));
+ spin_unlock(&j->lock);
/*
* If it's been longer than j->reclaim_delay_ms since we last flushed,
@@ -2110,7 +2132,7 @@ static int journal_write_alloc(struct journal *j, struct journal_buf *w,
ja->sectors_free = ca->mi.bucket_size - sectors;
ja->cur_idx = (ja->cur_idx + 1) % ja->nr;
- ja->bucket_seq[ja->cur_idx] = atomic64_read(&j->seq);
+ ja->bucket_seq[ja->cur_idx] = le64_to_cpu(w->data->seq);
extent_ptr_append(bkey_i_to_extent(&j->key),
(struct bch_extent_ptr) {
@@ -2436,9 +2458,9 @@ u64 bch2_inode_journal_seq(struct journal *j, u64 inode)
spin_lock(&j->lock);
if (test_bit(h, journal_cur_buf(j)->has_inode))
- seq = atomic64_read(&j->seq);
+ seq = journal_cur_seq(j);
else if (test_bit(h, journal_prev_buf(j)->has_inode))
- seq = atomic64_read(&j->seq) - 1;
+ seq = journal_cur_seq(j) - 1;
spin_unlock(&j->lock);
return seq;
@@ -2547,7 +2569,7 @@ u64 bch2_journal_last_unwritten_seq(struct journal *j)
u64 seq;
spin_lock(&j->lock);
- seq = atomic64_read(&j->seq);
+ seq = journal_cur_seq(j);
if (j->reservations.prev_buf_unwritten)
seq--;
spin_unlock(&j->lock);
@@ -2560,9 +2582,9 @@ int bch2_journal_open_seq_async(struct journal *j, u64 seq, struct closure *pare
int ret;
spin_lock(&j->lock);
- BUG_ON(seq > atomic64_read(&j->seq));
+ BUG_ON(seq > journal_cur_seq(j));
- if (seq < atomic64_read(&j->seq) ||
+ if (seq < journal_cur_seq(j) ||
journal_entry_is_open(j)) {
spin_unlock(&j->lock);
return 1;
@@ -2583,17 +2605,17 @@ void bch2_journal_wait_on_seq(struct journal *j, u64 seq, struct closure *parent
{
spin_lock(&j->lock);
- BUG_ON(seq > atomic64_read(&j->seq));
+ BUG_ON(seq > journal_cur_seq(j));
if (bch2_journal_error(j)) {
spin_unlock(&j->lock);
return;
}
- if (seq == atomic64_read(&j->seq)) {
+ if (seq == journal_cur_seq(j)) {
if (!closure_wait(&journal_cur_buf(j)->wait, parent))
BUG();
- } else if (seq + 1 == atomic64_read(&j->seq) &&
+ } else if (seq + 1 == journal_cur_seq(j) &&
j->reservations.prev_buf_unwritten) {
if (!closure_wait(&journal_prev_buf(j)->wait, parent))
BUG();
@@ -2615,14 +2637,14 @@ void bch2_journal_flush_seq_async(struct journal *j, u64 seq, struct closure *pa
spin_lock(&j->lock);
- BUG_ON(seq > atomic64_read(&j->seq));
+ BUG_ON(seq > journal_cur_seq(j));
if (bch2_journal_error(j)) {
spin_unlock(&j->lock);
return;
}
- if (seq == atomic64_read(&j->seq)) {
+ if (seq == journal_cur_seq(j)) {
bool set_need_write = false;
buf = journal_cur_buf(j);
@@ -2643,7 +2665,7 @@ void bch2_journal_flush_seq_async(struct journal *j, u64 seq, struct closure *pa
case JOURNAL_ENTRY_CLOSED:
/*
* Journal entry hasn't been opened yet, but caller
- * claims it has something (seq == j->seq):
+ * claims it has something
*/
BUG();
case JOURNAL_ENTRY_INUSE:
@@ -2652,7 +2674,7 @@ void bch2_journal_flush_seq_async(struct journal *j, u64 seq, struct closure *pa
return;
}
} else if (parent &&
- seq + 1 == atomic64_read(&j->seq) &&
+ seq + 1 == journal_cur_seq(j) &&
j->reservations.prev_buf_unwritten) {
buf = journal_prev_buf(j);
@@ -2676,9 +2698,9 @@ static int journal_seq_flushed(struct journal *j, u64 seq)
int ret = 1;
spin_lock(&j->lock);
- BUG_ON(seq > atomic64_read(&j->seq));
+ BUG_ON(seq > journal_cur_seq(j));
- if (seq == atomic64_read(&j->seq)) {
+ if (seq == journal_cur_seq(j)) {
bool set_need_write = false;
ret = 0;
@@ -2697,7 +2719,7 @@ static int journal_seq_flushed(struct journal *j, u64 seq)
case JOURNAL_ENTRY_CLOSED:
/*
* Journal entry hasn't been opened yet, but caller
- * claims it has something (seq == j->seq):
+ * claims it has something
*/
BUG();
case JOURNAL_ENTRY_INUSE:
@@ -2705,7 +2727,7 @@ static int journal_seq_flushed(struct journal *j, u64 seq)
case JOURNAL_UNLOCKED:
return 0;
}
- } else if (seq + 1 == atomic64_read(&j->seq) &&
+ } else if (seq + 1 == journal_cur_seq(j) &&
j->reservations.prev_buf_unwritten) {
ret = bch2_journal_error(j);
}
@@ -2762,7 +2784,7 @@ void bch2_journal_flush_async(struct journal *j, struct closure *parent)
u64 seq, journal_seq;
spin_lock(&j->lock);
- journal_seq = atomic64_read(&j->seq);
+ journal_seq = journal_cur_seq(j);
if (journal_entry_is_open(j)) {
seq = journal_seq;
@@ -2782,7 +2804,7 @@ int bch2_journal_flush(struct journal *j)
u64 seq, journal_seq;
spin_lock(&j->lock);
- journal_seq = atomic64_read(&j->seq);
+ journal_seq = journal_cur_seq(j);
if (journal_entry_is_open(j)) {
seq = journal_seq;
@@ -2797,7 +2819,7 @@ int bch2_journal_flush(struct journal *j)
return bch2_journal_flush_seq(j, seq);
}
-int bch2_journal_flush_device(struct journal *j, unsigned dev_idx)
+int bch2_journal_flush_device(struct journal *j, int dev_idx)
{
struct bch_fs *c = container_of(j, struct bch_fs, journal);
struct journal_entry_pin_list *p;
@@ -2807,7 +2829,9 @@ int bch2_journal_flush_device(struct journal *j, unsigned dev_idx)
spin_lock(&j->lock);
fifo_for_each_entry_ptr(p, &j->pin, iter)
- if (bch2_dev_list_has_dev(p->devs, dev_idx))
+ if (dev_idx >= 0
+ ? bch2_dev_list_has_dev(p->devs, dev_idx)
+ : p->devs.nr < c->opts.metadata_replicas)
seq = iter;
spin_unlock(&j->lock);
@@ -2821,7 +2845,7 @@ int bch2_journal_flush_device(struct journal *j, unsigned dev_idx)
seq = 0;
spin_lock(&j->lock);
- while (!ret && seq < atomic64_read(&j->seq)) {
+ while (!ret && seq < j->pin.back) {
seq = max(seq, journal_last_seq(j));
devs = journal_seq_pin(j, seq)->devs;
seq++;
@@ -2982,7 +3006,7 @@ ssize_t bch2_journal_print_debug(struct journal *j, char *buf)
"dirty:\t\t\t%i\n"
"replay done:\t\t%i\n",
fifo_used(&j->pin),
- (u64) atomic64_read(&j->seq),
+ journal_cur_seq(j),
journal_last_seq(j),
j->last_seq_ondisk,
journal_state_count(*s, s->idx),
diff --git a/libbcachefs/journal.h b/libbcachefs/journal.h
index 52d74ee..46ae8f0 100644
--- a/libbcachefs/journal.h
+++ b/libbcachefs/journal.h
@@ -368,7 +368,7 @@ void bch2_journal_meta_async(struct journal *, struct closure *);
int bch2_journal_flush_seq(struct journal *, u64);
int bch2_journal_flush(struct journal *);
int bch2_journal_meta(struct journal *);
-int bch2_journal_flush_device(struct journal *, unsigned);
+int bch2_journal_flush_device(struct journal *, int);
void bch2_journal_halt(struct journal *);
diff --git a/libbcachefs/migrate.c b/libbcachefs/migrate.c
index 01c8896..9c2920c 100644
--- a/libbcachefs/migrate.c
+++ b/libbcachefs/migrate.c
@@ -40,12 +40,15 @@ static int bch2_dev_usrdata_migrate(struct bch_fs *c, struct bch_dev *ca,
* operations
*/
do {
+ memset(&stats, 0, sizeof(stats));
+
ret = bch2_move_data(c, NULL,
SECTORS_IN_FLIGHT_PER_DEVICE,
NULL,
writepoint_hashed((unsigned long) current),
0,
ca->dev_idx,
+ POS_MIN, POS_MAX,
migrate_pred, ca,
&stats);
if (ret) {
diff --git a/libbcachefs/move.c b/libbcachefs/move.c
index a67e7a4..e5a46ba 100644
--- a/libbcachefs/move.c
+++ b/libbcachefs/move.c
@@ -87,7 +87,6 @@ static int bch2_migrate_index_update(struct bch_write_op *op)
m->move_dev)))
bch2_extent_drop_ptr(extent_i_to_s(insert), ptr);
-
extent_for_each_ptr_crc(extent_i_to_s(new), ptr, crc) {
if (bch2_extent_has_device(extent_i_to_s_c(insert), ptr->dev)) {
/*
@@ -194,6 +193,8 @@ static void move_free(struct closure *cl)
struct bio_vec *bv;
int i;
+ bch2_disk_reservation_put(io->write.op.c, &io->write.op.res);
+
bio_for_each_segment_all(bv, &io->write.op.wbio.bio, i)
if (bv->bv_page)
__free_page(bv->bv_page);
@@ -243,20 +244,21 @@ static int bch2_move_extent(struct bch_fs *c,
int btree_insert_flags,
int move_device,
struct bch_io_opts opts,
- struct bkey_s_c k)
+ struct bkey_s_c_extent e)
{
struct extent_pick_ptr pick;
struct moving_io *io;
const struct bch_extent_ptr *ptr;
struct bch_extent_crc_unpacked crc;
- unsigned sectors = k.k->size, pages;
+ unsigned sectors = e.k->size, pages, nr_good;
+ int ret = -ENOMEM;
- bch2_extent_pick_ptr(c, k, NULL, &pick);
+ bch2_extent_pick_ptr(c, e.s_c, NULL, &pick);
if (IS_ERR_OR_NULL(pick.ca))
return pick.ca ? PTR_ERR(pick.ca) : 0;
/* write path might have to decompress data: */
- extent_for_each_ptr_crc(bkey_s_c_to_extent(k), ptr, crc)
+ extent_for_each_ptr_crc(e, ptr, crc)
sectors = max_t(unsigned, sectors, crc.uncompressed_size);
pages = DIV_ROUND_UP(sectors, PAGE_SECTORS);
@@ -266,7 +268,7 @@ static int bch2_move_extent(struct bch_fs *c,
goto err;
io->write.ctxt = ctxt;
- io->sectors = k.k->size;
+ io->sectors = e.k->size;
bio_init(&io->write.op.wbio.bio, io->bi_inline_vecs, pages);
bio_set_prio(&io->write.op.wbio.bio,
@@ -274,10 +276,8 @@ static int bch2_move_extent(struct bch_fs *c,
io->write.op.wbio.bio.bi_iter.bi_size = sectors << 9;
bch2_bio_map(&io->write.op.wbio.bio, NULL);
- if (bio_alloc_pages(&io->write.op.wbio.bio, GFP_KERNEL)) {
- kfree(io);
- goto err;
- }
+ if (bio_alloc_pages(&io->write.op.wbio.bio, GFP_KERNEL))
+ goto err_free;
io->rbio.opts = opts;
bio_init(&io->rbio.bio, io->bi_inline_vecs, pages);
@@ -285,7 +285,7 @@ static int bch2_move_extent(struct bch_fs *c,
io->rbio.bio.bi_iter.bi_size = sectors << 9;
bio_set_op_attrs(&io->rbio.bio, REQ_OP_READ, 0);
- io->rbio.bio.bi_iter.bi_sector = bkey_start_offset(k.k);
+ io->rbio.bio.bi_iter.bi_sector = bkey_start_offset(e.k);
io->rbio.bio.bi_end_io = move_read_endio;
io->write.btree_insert_flags = btree_insert_flags;
@@ -298,10 +298,22 @@ static int bch2_move_extent(struct bch_fs *c,
io->write.op.devs = devs;
io->write.op.write_point = wp;
+ if (move_device < 0 &&
+ ((nr_good = bch2_extent_nr_good_ptrs(c, e)) <
+ c->opts.data_replicas)) {
+ io->write.op.nr_replicas = c->opts.data_replicas - nr_good;
+
+ ret = bch2_disk_reservation_get(c, &io->write.op.res,
+ e.k->size,
+ io->write.op.nr_replicas, 0);
+ if (ret)
+ goto err_free_pages;
+ }
+
atomic64_inc(&ctxt->stats->keys_moved);
- atomic64_add(k.k->size, &ctxt->stats->sectors_moved);
+ atomic64_add(e.k->size, &ctxt->stats->sectors_moved);
- trace_move_extent(k.k);
+ trace_move_extent(e.k);
atomic_add(io->sectors, &ctxt->sectors_in_flight);
list_add_tail(&io->list, &ctxt->reads);
@@ -311,12 +323,16 @@ static int bch2_move_extent(struct bch_fs *c,
* ctxt when doing wakeup
*/
closure_get(&ctxt->cl);
- bch2_read_extent(c, &io->rbio, bkey_s_c_to_extent(k),
- &pick, BCH_READ_NODECODE);
+ bch2_read_extent(c, &io->rbio, e, &pick, BCH_READ_NODECODE);
return 0;
+err_free_pages:
+ bio_free_pages(&io->write.op.wbio.bio);
+err_free:
+ kfree(io);
err:
- trace_move_alloc_fail(k.k);
- return -ENOMEM;
+ percpu_ref_put(&pick.ca->io_ref);
+ trace_move_alloc_fail(e.k);
+ return ret;
}
static void do_pending_writes(struct moving_context *ctxt)
@@ -355,6 +371,8 @@ int bch2_move_data(struct bch_fs *c,
struct write_point_specifier wp,
int btree_insert_flags,
int move_device,
+ struct bpos start,
+ struct bpos end,
move_pred_fn pred, void *arg,
struct bch_move_stats *stats)
{
@@ -363,14 +381,16 @@ int bch2_move_data(struct bch_fs *c,
struct bch_io_opts opts = bch2_opts_to_inode_opts(c->opts);
BKEY_PADDED(k) tmp;
struct bkey_s_c k;
+ struct bkey_s_c_extent e;
u64 cur_inum = U64_MAX;
int ret = 0;
- memset(stats, 0, sizeof(*stats));
closure_init_stack(&ctxt.cl);
INIT_LIST_HEAD(&ctxt.reads);
init_waitqueue_head(&ctxt.wait);
- bch2_btree_iter_init(&stats->iter, c, BTREE_ID_EXTENTS, POS_MIN,
+
+ stats->data_type = BCH_DATA_USER;
+ bch2_btree_iter_init(&stats->iter, c, BTREE_ID_EXTENTS, start,
BTREE_ITER_PREFETCH);
if (rate)
@@ -396,10 +416,14 @@ peek:
ret = btree_iter_err(k);
if (ret)
break;
+ if (bkey_cmp(bkey_start_pos(k.k), end) >= 0)
+ break;
if (!bkey_extent_is_data(k.k))
goto next_nondata;
+ e = bkey_s_c_to_extent(k);
+
if (cur_inum != k.k->p.inode) {
struct bch_inode_unpacked inode;
@@ -413,7 +437,7 @@ peek:
goto peek;
}
- if (!pred(arg, bkey_s_c_to_extent(k)))
+ if (!pred(arg, e))
goto next;
/* unlock before doing IO: */
@@ -423,7 +447,8 @@ peek:
if (bch2_move_extent(c, &ctxt, devs, wp,
btree_insert_flags,
- move_device, opts, k)) {
+ move_device, opts,
+ bkey_s_c_to_extent(k))) {
/* memory allocation failure, wait for some IO to finish */
bch2_move_ctxt_wait_for_io(&ctxt);
continue;
@@ -453,3 +478,157 @@ next_nondata:
return ret;
}
+
+static int bch2_gc_data_replicas(struct bch_fs *c)
+{
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ int ret;
+
+ mutex_lock(&c->replicas_gc_lock);
+ bch2_replicas_gc_start(c, 1 << BCH_DATA_USER);
+
+ for_each_btree_key(&iter, c, BTREE_ID_EXTENTS, POS_MIN,
+ BTREE_ITER_PREFETCH, k) {
+ ret = bch2_check_mark_super(c, BCH_DATA_USER, bch2_bkey_devs(k));
+ if (ret)
+ break;
+ }
+ ret = bch2_btree_iter_unlock(&iter) ?: ret;
+
+ bch2_replicas_gc_end(c, ret);
+ mutex_unlock(&c->replicas_gc_lock);
+
+ return ret;
+}
+
+static int bch2_gc_btree_replicas(struct bch_fs *c)
+{
+ struct btree_iter iter;
+ struct btree *b;
+ unsigned id;
+ int ret = 0;
+
+ mutex_lock(&c->replicas_gc_lock);
+ bch2_replicas_gc_start(c, 1 << BCH_DATA_BTREE);
+
+ for (id = 0; id < BTREE_ID_NR; id++) {
+ for_each_btree_node(&iter, c, id, POS_MIN, BTREE_ITER_PREFETCH, b) {
+ ret = bch2_check_mark_super(c, BCH_DATA_BTREE,
+ bch2_bkey_devs(bkey_i_to_s_c(&b->key)));
+
+ bch2_btree_iter_cond_resched(&iter);
+ }
+
+ ret = bch2_btree_iter_unlock(&iter) ?: ret;
+ }
+
+ bch2_replicas_gc_end(c, ret);
+ mutex_unlock(&c->replicas_gc_lock);
+
+ return ret;
+}
+
+static int bch2_move_btree(struct bch_fs *c,
+ move_pred_fn pred,
+ void *arg,
+ struct bch_move_stats *stats)
+{
+ struct btree *b;
+ unsigned id;
+ int ret = 0;
+
+ stats->data_type = BCH_DATA_BTREE;
+
+ for (id = 0; id < BTREE_ID_NR; id++) {
+ for_each_btree_node(&stats->iter, c, id, POS_MIN, BTREE_ITER_PREFETCH, b) {
+ if (pred(arg, bkey_i_to_s_c_extent(&b->key)))
+ ret = bch2_btree_node_rewrite(c, &stats->iter,
+ b->data->keys.seq, 0) ?: ret;
+
+ bch2_btree_iter_cond_resched(&stats->iter);
+ }
+
+ ret = bch2_btree_iter_unlock(&stats->iter) ?: ret;
+ }
+
+ return ret;
+}
+
+#if 0
+static bool scrub_data_pred(void *arg, struct bkey_s_c_extent e)
+{
+}
+#endif
+
+static bool rereplicate_metadata_pred(void *arg, struct bkey_s_c_extent e)
+{
+ struct bch_fs *c = arg;
+ unsigned nr_good = bch2_extent_nr_good_ptrs(c, e);
+
+ return nr_good && nr_good < c->opts.metadata_replicas;
+}
+
+static bool rereplicate_data_pred(void *arg, struct bkey_s_c_extent e)
+{
+ struct bch_fs *c = arg;
+ unsigned nr_good = bch2_extent_nr_good_ptrs(c, e);
+
+ return nr_good && nr_good < c->opts.data_replicas;
+}
+
+static bool migrate_pred(void *arg, struct bkey_s_c_extent e)
+{
+ struct bch_ioctl_data *op = arg;
+
+ return bch2_extent_has_device(e, op->migrate.dev);
+}
+
+int bch2_data_job(struct bch_fs *c,
+ struct bch_move_stats *stats,
+ struct bch_ioctl_data op)
+{
+ int ret = 0;
+
+ switch (op.op) {
+ case BCH_DATA_OP_REREPLICATE:
+ stats->data_type = BCH_DATA_JOURNAL;
+ ret = bch2_journal_flush_device(&c->journal, -1);
+
+ ret = bch2_move_btree(c, rereplicate_metadata_pred, c, stats) ?: ret;
+ ret = bch2_gc_btree_replicas(c) ?: ret;
+
+ ret = bch2_move_data(c, NULL, SECTORS_IN_FLIGHT_PER_DEVICE,
+ NULL,
+ writepoint_hashed((unsigned long) current),
+ 0, -1,
+ op.start,
+ op.end,
+ rereplicate_data_pred, c, stats) ?: ret;
+ ret = bch2_gc_data_replicas(c) ?: ret;
+ break;
+ case BCH_DATA_OP_MIGRATE:
+ if (op.migrate.dev >= c->sb.nr_devices)
+ return -EINVAL;
+
+ stats->data_type = BCH_DATA_JOURNAL;
+ ret = bch2_journal_flush_device(&c->journal, op.migrate.dev);
+
+ ret = bch2_move_btree(c, migrate_pred, &op, stats) ?: ret;
+ ret = bch2_gc_btree_replicas(c) ?: ret;
+
+ ret = bch2_move_data(c, NULL, SECTORS_IN_FLIGHT_PER_DEVICE,
+ NULL,
+ writepoint_hashed((unsigned long) current),
+ 0, -1,
+ op.start,
+ op.end,
+ migrate_pred, &op, stats) ?: ret;
+ ret = bch2_gc_data_replicas(c) ?: ret;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
diff --git a/libbcachefs/move.h b/libbcachefs/move.h
index 24d6ddf..07aa566 100644
--- a/libbcachefs/move.h
+++ b/libbcachefs/move.h
@@ -27,6 +27,7 @@ void bch2_migrate_write_init(struct migrate_write *, struct bch_read_bio *);
typedef bool (*move_pred_fn)(void *, struct bkey_s_c_extent);
struct bch_move_stats {
+ enum bch_data_type data_type;
struct btree_iter iter;
atomic64_t keys_moved;
@@ -38,7 +39,12 @@ struct bch_move_stats {
int bch2_move_data(struct bch_fs *, struct bch_ratelimit *,
unsigned, struct bch_devs_mask *,
struct write_point_specifier,
- int, int, move_pred_fn, void *,
+ int, int, struct bpos, struct bpos,
+ move_pred_fn, void *,
struct bch_move_stats *);
+int bch2_data_job(struct bch_fs *,
+ struct bch_move_stats *,
+ struct bch_ioctl_data);
+
#endif /* _BCACHEFS_MOVE_H */
diff --git a/libbcachefs/movinggc.c b/libbcachefs/movinggc.c
index d6f2968..515d500 100644
--- a/libbcachefs/movinggc.c
+++ b/libbcachefs/movinggc.c
@@ -106,6 +106,7 @@ static void bch2_copygc(struct bch_fs *c, struct bch_dev *ca)
size_t b;
int ret;
+ memset(&move_stats, 0, sizeof(move_stats));
closure_wait_event(&c->freelist_wait, have_copygc_reserve(ca));
/*
@@ -166,6 +167,7 @@ static void bch2_copygc(struct bch_fs *c, struct bch_dev *ca)
writepoint_ptr(&ca->copygc_write_point),
BTREE_INSERT_USE_RESERVE,
ca->dev_idx,
+ POS_MIN, POS_MAX,
copygc_pred, ca,
&move_stats);
diff --git a/libbcachefs/super-io.c b/libbcachefs/super-io.c
index 1f266ba..f333b8f 100644
--- a/libbcachefs/super-io.c
+++ b/libbcachefs/super-io.c
@@ -13,6 +13,7 @@
static int bch2_sb_replicas_to_cpu_replicas(struct bch_fs *);
static int bch2_cpu_replicas_to_sb_replicas(struct bch_fs *,
struct bch_replicas_cpu *);
+static int bch2_sb_disk_groups_to_cpu(struct bch_fs *);
/* superblock fields (optional/variable size sections: */
@@ -43,6 +44,7 @@ static const struct bch_sb_field_ops bch2_sb_field_ops[] = {
static const char *bch2_sb_field_validate(struct bch_sb *sb,
struct bch_sb_field *f)
+
{
unsigned type = le32_to_cpu(f->type);
@@ -297,7 +299,7 @@ const char *bch2_sb_validate(struct bch_sb_handle *disk_sb)
if (!sb->nr_devices ||
sb->nr_devices <= sb->dev_idx ||
sb->nr_devices > BCH_SB_MEMBERS_MAX)
- return "Bad cache device number in set";
+ return "Bad number of member devices";
if (!BCH_SB_META_REPLICAS_WANT(sb) ||
BCH_SB_META_REPLICAS_WANT(sb) >= BCH_REPLICAS_MAX)
@@ -458,6 +460,10 @@ int bch2_sb_to_fs(struct bch_fs *c, struct bch_sb *src)
if (ret)
return ret;
+ ret = bch2_sb_disk_groups_to_cpu(c);
+ if (ret)
+ return ret;
+
bch2_sb_update(c);
return 0;
}
@@ -1557,3 +1563,129 @@ static const char *bch2_sb_validate_quota(struct bch_sb *sb,
return NULL;
}
+
+/* Disk groups: */
+
+#if 0
+static size_t trim_nulls(const char *str, size_t len)
+{
+ while (len && !str[len - 1])
+ --len;
+ return len;
+}
+#endif
+
+static const char *bch2_sb_validate_disk_groups(struct bch_sb *sb,
+ struct bch_sb_field *f)
+{
+ struct bch_sb_field_disk_groups *groups =
+ field_to_type(f, disk_groups);
+ struct bch_sb_field_members *mi;
+ struct bch_member *m;
+ struct bch_disk_group *g;
+ unsigned nr_groups;
+
+ mi = bch2_sb_get_members(sb);
+ groups = bch2_sb_get_disk_groups(sb);
+ nr_groups = disk_groups_nr(groups);
+
+ for (m = mi->members;
+ m < mi->members + sb->nr_devices;
+ m++) {
+ if (!BCH_MEMBER_GROUP(m))
+ continue;
+
+ if (BCH_MEMBER_GROUP(m) >= nr_groups)
+ return "disk has invalid group";
+
+ g = &groups->entries[BCH_MEMBER_GROUP(m)];
+ if (BCH_GROUP_DELETED(g))
+ return "disk has invalid group";
+ }
+#if 0
+ if (!groups)
+ return NULL;
+
+ char **labels;
+ labels = kcalloc(nr_groups, sizeof(char *), GFP_KERNEL);
+ if (!labels)
+ return "cannot allocate memory";
+
+ for (g = groups->groups;
+ g < groups->groups + nr_groups;
+ g++) {
+
+ }
+#endif
+ return NULL;
+}
+
+static int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
+{
+ struct bch_sb_field_members *mi;
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_disk_groups_cpu *cpu_g, *old_g;
+ unsigned i, nr_groups;
+
+ lockdep_assert_held(&c->sb_lock);
+
+ mi = bch2_sb_get_members(c->disk_sb);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+ nr_groups = disk_groups_nr(groups);
+
+ if (!groups)
+ return 0;
+
+ cpu_g = kzalloc(sizeof(*cpu_g) +
+ sizeof(cpu_g->entries[0]) * nr_groups, GFP_KERNEL);
+ if (!cpu_g)
+ return -ENOMEM;
+
+ cpu_g->nr = nr_groups;
+
+ for (i = 0; i < nr_groups; i++) {
+ struct bch_disk_group *src = &groups->entries[i];
+ struct bch_disk_group_cpu *dst = &cpu_g->entries[i];
+
+ dst->deleted = BCH_GROUP_DELETED(src);
+ }
+
+ for (i = 0; i < c->disk_sb->nr_devices; i++) {
+ struct bch_member *m = mi->members + i;
+ struct bch_disk_group_cpu *dst =
+ &cpu_g->entries[BCH_MEMBER_GROUP(m)];
+
+ if (!bch2_member_exists(m))
+ continue;
+
+ __set_bit(i, dst->devs.d);
+ }
+
+ old_g = c->disk_groups;
+ rcu_assign_pointer(c->disk_groups, cpu_g);
+ if (old_g)
+ kfree_rcu(old_g, rcu);
+
+ return 0;
+}
+
+const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
+{
+ struct target t = target_decode(target);
+
+ switch (t.type) {
+ case TARGET_DEV:
+ BUG_ON(t.dev >= c->sb.nr_devices && !c->devs[t.dev]);
+ return &c->devs[t.dev]->self;
+ case TARGET_GROUP: {
+ struct bch_disk_groups_cpu *g =
+ rcu_dereference(c->disk_groups);
+
+ /* XXX: what to do here? */
+ BUG_ON(t.group >= g->nr || g->entries[t.group].deleted);
+ return &g->entries[t.group].devs;
+ }
+ default:
+ BUG();
+ }
+}
diff --git a/libbcachefs/super-io.h b/libbcachefs/super-io.h
index 59a8b81..eb85410 100644
--- a/libbcachefs/super-io.h
+++ b/libbcachefs/super-io.h
@@ -127,6 +127,7 @@ static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
.nbuckets = le64_to_cpu(mi->nbuckets),
.first_bucket = le16_to_cpu(mi->first_bucket),
.bucket_size = le16_to_cpu(mi->bucket_size),
+ .group = BCH_MEMBER_GROUP(mi),
.state = BCH_MEMBER_STATE(mi),
.tier = BCH_MEMBER_TIER(mi),
.replacement = BCH_MEMBER_REPLACEMENT(mi),
@@ -177,4 +178,65 @@ replicas_entry_next(struct bch_replicas_entry *i)
(void *) (_i) < vstruct_end(&(_r)->field) && (_i)->data_type;\
(_i) = replicas_entry_next(_i))
+/* disk groups: */
+
+static inline unsigned disk_groups_nr(struct bch_sb_field_disk_groups *groups)
+{
+ return groups
+ ? (vstruct_end(&groups->field) -
+ (void *) &groups->entries[0]) / sizeof(struct bch_disk_group)
+ : 0;
+}
+
+struct target {
+ enum {
+ TARGET_NULL,
+ TARGET_DEV,
+ TARGET_GROUP,
+ } type;
+ union {
+ unsigned dev;
+ unsigned group;
+ };
+};
+
+static inline u16 dev_to_target(unsigned dev)
+{
+ return 1 + dev;
+}
+
+static inline u16 group_to_target(unsigned group)
+{
+ return 1 + U8_MAX + group;
+}
+
+static inline struct target target_decode(unsigned target)
+{
+ if (!target)
+ return (struct target) { .type = TARGET_NULL };
+
+ --target;
+ if (target <= U8_MAX)
+ return (struct target) { .type = TARGET_DEV, .dev = target };
+
+ target -= U8_MAX;
+ return (struct target) { .type = TARGET_GROUP, .group = target };
+}
+
+static inline bool dev_in_target(struct bch_dev *ca, unsigned target)
+{
+ struct target t = target_decode(target);
+
+ switch (t.type) {
+ case TARGET_DEV:
+ return ca->dev_idx == t.dev;
+ case TARGET_GROUP:
+ return ca->mi.group && ca->mi.group == t.group;
+ default:
+ BUG();
+ }
+}
+
+const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *, unsigned);
+
#endif /* _BCACHEFS_SUPER_IO_H */
diff --git a/libbcachefs/super.c b/libbcachefs/super.c
index 8c7a147..f836c19 100644
--- a/libbcachefs/super.c
+++ b/libbcachefs/super.c
@@ -426,6 +426,7 @@ static void bch2_fs_free(struct bch_fs *c)
mempool_exit(&c->fill_iter);
percpu_ref_exit(&c->writes);
kfree(rcu_dereference_protected(c->replicas, 1));
+ kfree(rcu_dereference_protected(c->disk_groups, 1));
if (c->copygc_wq)
destroy_workqueue(c->copygc_wq);
@@ -1169,6 +1170,12 @@ static int __bch2_dev_online(struct bch_fs *c, struct bch_sb_handle *sb)
BUG_ON(!percpu_ref_is_zero(&ca->io_ref));
+ if (get_capacity(sb->bdev->bd_disk) <
+ ca->mi.bucket_size * ca->mi.nbuckets) {
+ bch_err(c, "device too small");
+ return -EINVAL;
+ }
+
ret = bch2_dev_journal_init(ca, sb->sb);
if (ret)
return ret;
@@ -1495,10 +1502,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
mutex_lock(&c->state_lock);
mutex_lock(&c->sb_lock);
- /*
- * Preserve the old cache member information (esp. tier)
- * before we start bashing the disk stuff.
- */
+ /* Grab member info for new disk: */
dev_mi = bch2_sb_get_members(sb.sb);
saved_mi = dev_mi->members[sb.sb->dev_idx];
saved_mi.last_mount = cpu_to_le64(ktime_get_seconds());
@@ -1646,47 +1650,6 @@ int bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca, int flags)
return 0;
}
-int bch2_dev_evacuate(struct bch_fs *c, struct bch_dev *ca)
-{
- unsigned data;
- int ret = 0;
-
- mutex_lock(&c->state_lock);
-
- if (ca->mi.state == BCH_MEMBER_STATE_RW &&
- bch2_dev_is_online(ca)) {
- bch_err(ca, "Cannot migrate data off RW device");
- ret = -EINVAL;
- goto err;
- }
-
- ret = bch2_dev_data_migrate(c, ca, 0);
- if (ret) {
- bch_err(ca, "Error migrating data: %i", ret);
- goto err;
- }
-
- ret = bch2_journal_flush_device(&c->journal, ca->dev_idx);
- if (ret) {
- bch_err(ca, "Migrate failed: error %i flushing journal", ret);
- goto err;
- }
-
- data = bch2_dev_has_data(c, ca);
- if (data) {
- char buf[100];
-
- bch2_scnprint_flag_list(buf, sizeof(buf),
- bch2_data_types, data);
- bch_err(ca, "Migrate failed, still has data (%s)", buf);
- ret = -EINVAL;
- goto err;
- }
-err:
- mutex_unlock(&c->state_lock);
- return ret;
-}
-
int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
{
struct bch_member *mi;
diff --git a/libbcachefs/super.h b/libbcachefs/super.h
index a35ee3d..d0a38cf 100644
--- a/libbcachefs/super.h
+++ b/libbcachefs/super.h
@@ -30,7 +30,7 @@ static inline bool bch2_dev_is_online(struct bch_dev *ca)
return ca->disk_sb.bdev != NULL;
}
-static inline unsigned dev_mask_nr(struct bch_devs_mask *devs)
+static inline unsigned dev_mask_nr(const struct bch_devs_mask *devs)
{
return bitmap_weight(devs->d, BCH_SB_MEMBERS_MAX);
}
@@ -68,7 +68,7 @@ static inline void bch2_dev_list_add_dev(struct bch_devs_list *devs,
}
static inline struct bch_dev *__bch2_next_dev(struct bch_fs *c, unsigned *iter,
- struct bch_devs_mask *mask)
+ const struct bch_devs_mask *mask)
{
struct bch_dev *ca = NULL;
@@ -188,7 +188,6 @@ int bch2_dev_remove(struct bch_fs *, struct bch_dev *, int);
int bch2_dev_add(struct bch_fs *, const char *);
int bch2_dev_online(struct bch_fs *, const char *);
int bch2_dev_offline(struct bch_fs *, struct bch_dev *, int);
-int bch2_dev_evacuate(struct bch_fs *, struct bch_dev *);
int bch2_dev_resize(struct bch_fs *, struct bch_dev *, u64);
bool bch2_fs_emergency_read_only(struct bch_fs *);
diff --git a/libbcachefs/super_types.h b/libbcachefs/super_types.h
index 35c8beb..966da4a 100644
--- a/libbcachefs/super_types.h
+++ b/libbcachefs/super_types.h
@@ -22,6 +22,7 @@ struct bch_member_cpu {
u64 nbuckets; /* device size */
u16 first_bucket; /* index of first bucket used */
u16 bucket_size; /* sectors */
+ u16 group;
u8 state;
u8 tier;
u8 replacement;
@@ -42,4 +43,15 @@ struct bch_replicas_cpu {
struct bch_replicas_cpu_entry entries[];
};
+struct bch_disk_group_cpu {
+ struct bch_devs_mask devs;
+ bool deleted;
+};
+
+struct bch_disk_groups_cpu {
+ struct rcu_head rcu;
+ unsigned nr;
+ struct bch_disk_group_cpu entries[];
+};
+
#endif /* _BCACHEFS_SUPER_TYPES_H */
diff --git a/libbcachefs/tier.c b/libbcachefs/tier.c
index 6a58109..c4625c8 100644
--- a/libbcachefs/tier.c
+++ b/libbcachefs/tier.c
@@ -44,6 +44,7 @@ static int bch2_tiering_thread(void *arg)
unsigned long last;
unsigned i, nr_devices;
+ memset(&move_stats, 0, sizeof(move_stats));
set_freezable();
while (!kthread_should_stop()) {
@@ -91,6 +92,7 @@ static int bch2_tiering_thread(void *arg)
writepoint_ptr(&tier->wp),
0,
-1,
+ POS_MIN, POS_MAX,
tiering_pred, tier,
&move_stats);
}
diff --git a/linux/bio.c b/linux/bio.c
index d825698..79f50dc 100644
--- a/linux/bio.c
+++ b/linux/bio.c
@@ -163,6 +163,15 @@ struct bio *bio_split(struct bio *bio, int sectors,
return split;
}
+void bio_free_pages(struct bio *bio)
+{
+ struct bio_vec *bvec;
+ int i;
+
+ bio_for_each_segment_all(bvec, bio, i)
+ __free_page(bvec->bv_page);
+}
+
int bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
{
int i;