summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2017-12-03 19:29:13 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-02-07 06:59:56 -0500
commitee93aed2dddb8a3f87133e9f2d7070d1781e6838 (patch)
tree6aff95a90db3e4a00619f8ae1cc9f5cdd46e35e4
parent496cbe9474173ec41bf221dc8ab1f5d70a128c3b (diff)
bcachefs: Add superblock field for disk groups
-rw-r--r--fs/bcachefs/bcachefs.h2
-rw-r--r--fs/bcachefs/bcachefs_format.h40
-rw-r--r--fs/bcachefs/extents.c28
-rw-r--r--fs/bcachefs/extents.h4
-rw-r--r--fs/bcachefs/super-io.c134
-rw-r--r--fs/bcachefs/super-io.h62
-rw-r--r--fs/bcachefs/super.c12
-rw-r--r--fs/bcachefs/super.h4
-rw-r--r--fs/bcachefs/super_types.h12
9 files changed, 273 insertions, 25 deletions
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 298f26d4161d..cb9906c5bd22 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/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/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index cb9e450ba286..85f728f21295 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/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/fs/bcachefs/extents.c b/fs/bcachefs/extents.c
index c2469167efea..f5dccfad15d6 100644
--- a/fs/bcachefs/extents.c
+++ b/fs/bcachefs/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/fs/bcachefs/extents.h b/fs/bcachefs/extents.h
index eda34381001e..e8f54f2e9acb 100644
--- a/fs/bcachefs/extents.h
+++ b/fs/bcachefs/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/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index 1f266ba37386..f333b8fad58a 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/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/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index 59a8b816c467..eb85410c5f16 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/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/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 8c7a147a0833..4c8b20354e13 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/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());
diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h
index a35ee3db0ea2..751ee20ae0b5 100644
--- a/fs/bcachefs/super.h
+++ b/fs/bcachefs/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;
diff --git a/fs/bcachefs/super_types.h b/fs/bcachefs/super_types.h
index 35c8bebf683f..966da4afbeda 100644
--- a/fs/bcachefs/super_types.h
+++ b/fs/bcachefs/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 */