diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2017-12-03 19:29:13 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-02-07 06:59:56 -0500 |
commit | ee93aed2dddb8a3f87133e9f2d7070d1781e6838 (patch) | |
tree | 6aff95a90db3e4a00619f8ae1cc9f5cdd46e35e4 | |
parent | 496cbe9474173ec41bf221dc8ab1f5d70a128c3b (diff) |
bcachefs: Add superblock field for disk groups
-rw-r--r-- | fs/bcachefs/bcachefs.h | 2 | ||||
-rw-r--r-- | fs/bcachefs/bcachefs_format.h | 40 | ||||
-rw-r--r-- | fs/bcachefs/extents.c | 28 | ||||
-rw-r--r-- | fs/bcachefs/extents.h | 4 | ||||
-rw-r--r-- | fs/bcachefs/super-io.c | 134 | ||||
-rw-r--r-- | fs/bcachefs/super-io.h | 62 | ||||
-rw-r--r-- | fs/bcachefs/super.c | 12 | ||||
-rw-r--r-- | fs/bcachefs/super.h | 4 | ||||
-rw-r--r-- | fs/bcachefs/super_types.h | 12 |
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 */ |