Commit 3d48a7f8 authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: KEY_TYPE_alloc_v4

This introduces a new alloc key which doesn't use varints. Soon we'll be
adding backpointers and storing them in alloc keys, which means our
pack/unpack workflow for alloc keys won't really work - we'll need to be
mutating alloc keys in place.

Instead of bch2_alloc_unpack(), we now have bch2_alloc_to_v4() that
converts older types of alloc keys to v4 if needed.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent d326ab2f
This diff is collapsed.
...@@ -10,48 +10,14 @@ ...@@ -10,48 +10,14 @@
extern const char * const bch2_allocator_states[]; extern const char * const bch2_allocator_states[];
struct bkey_alloc_unpacked {
u64 journal_seq;
u64 bucket;
u8 dev;
u8 gen;
u8 oldest_gen;
u8 data_type;
#define x(_name, _bits) u##_bits _name;
BCH_ALLOC_FIELDS_V2()
#undef x
};
/* How out of date a pointer gen is allowed to be: */ /* How out of date a pointer gen is allowed to be: */
#define BUCKET_GC_GEN_MAX 96U #define BUCKET_GC_GEN_MAX 96U
/* returns true if not equal */ struct bkey_i_alloc_v4 *
static inline bool bkey_alloc_unpacked_cmp(struct bkey_alloc_unpacked l, bch2_trans_start_alloc_update(struct btree_trans *, struct btree_iter *, struct bpos);
struct bkey_alloc_unpacked r)
{
return l.gen != r.gen ||
l.oldest_gen != r.oldest_gen ||
l.data_type != r.data_type
#define x(_name, ...) || l._name != r._name
BCH_ALLOC_FIELDS_V2()
#undef x
;
}
struct bkey_alloc_buf {
struct bkey_i k;
struct bch_alloc_v3 v;
#define x(_name, _bits) + _bits / 8
u8 _pad[0 + BCH_ALLOC_FIELDS_V2()];
#undef x
} __attribute__((packed, aligned(8)));
struct bkey_alloc_unpacked bch2_alloc_unpack(struct bkey_s_c); void bch2_alloc_to_v4(struct bkey_s_c, struct bch_alloc_v4 *);
struct bkey_alloc_buf *bch2_alloc_pack(struct btree_trans *, struct bkey_i_alloc_v4 *bch2_alloc_to_v4_mut(struct btree_trans *, struct bkey_s_c);
const struct bkey_alloc_unpacked);
int bch2_alloc_write(struct btree_trans *, struct btree_iter *,
struct bkey_alloc_unpacked *, unsigned);
int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int); int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int);
...@@ -60,6 +26,8 @@ int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int); ...@@ -60,6 +26,8 @@ int bch2_bucket_io_time_reset(struct btree_trans *, unsigned, size_t, int);
const char *bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c); const char *bch2_alloc_v1_invalid(const struct bch_fs *, struct bkey_s_c);
const char *bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c); const char *bch2_alloc_v2_invalid(const struct bch_fs *, struct bkey_s_c);
const char *bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c); const char *bch2_alloc_v3_invalid(const struct bch_fs *, struct bkey_s_c);
const char *bch2_alloc_v4_invalid(const struct bch_fs *, struct bkey_s_c k);
void bch2_alloc_v4_swab(struct bkey_s);
void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_alloc (struct bkey_ops) { \ #define bch2_bkey_ops_alloc (struct bkey_ops) { \
...@@ -80,6 +48,13 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); ...@@ -80,6 +48,13 @@ void bch2_alloc_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
.atomic_trigger = bch2_mark_alloc, \ .atomic_trigger = bch2_mark_alloc, \
} }
#define bch2_bkey_ops_alloc_v4 (struct bkey_ops) { \
.key_invalid = bch2_alloc_v4_invalid, \
.val_to_text = bch2_alloc_to_text, \
.swab = bch2_alloc_v4_swab, \
.atomic_trigger = bch2_mark_alloc, \
}
static inline bool bkey_is_alloc(const struct bkey *k) static inline bool bkey_is_alloc(const struct bkey *k)
{ {
return k->type == KEY_TYPE_alloc || return k->type == KEY_TYPE_alloc ||
......
...@@ -82,6 +82,21 @@ ...@@ -82,6 +82,21 @@
typedef uuid_t __uuid_t; typedef uuid_t __uuid_t;
#endif #endif
#define BITMASK(name, type, field, offset, end) \
static const unsigned name##_OFFSET = offset; \
static const unsigned name##_BITS = (end - offset); \
\
static inline __u64 name(const type *k) \
{ \
return (k->field >> offset) & ~(~0ULL << (end - offset)); \
} \
\
static inline void SET_##name(type *k, __u64 v) \
{ \
k->field &= ~(~(~0ULL << (end - offset)) << offset); \
k->field |= (v & ~(~0ULL << (end - offset))) << offset; \
}
#define LE_BITMASK(_bits, name, type, field, offset, end) \ #define LE_BITMASK(_bits, name, type, field, offset, end) \
static const unsigned name##_OFFSET = offset; \ static const unsigned name##_OFFSET = offset; \
static const unsigned name##_BITS = (end - offset); \ static const unsigned name##_BITS = (end - offset); \
...@@ -353,7 +368,8 @@ static inline void bkey_init(struct bkey *k) ...@@ -353,7 +368,8 @@ static inline void bkey_init(struct bkey *k)
x(inode_v2, 23) \ x(inode_v2, 23) \
x(alloc_v3, 24) \ x(alloc_v3, 24) \
x(set, 25) \ x(set, 25) \
x(lru, 26) x(lru, 26) \
x(alloc_v4, 27)
enum bch_bkey_type { enum bch_bkey_type {
#define x(name, nr) KEY_TYPE_##name = nr, #define x(name, nr) KEY_TYPE_##name = nr,
...@@ -903,6 +919,30 @@ struct bch_alloc_v3 { ...@@ -903,6 +919,30 @@ struct bch_alloc_v3 {
__u8 data[]; __u8 data[];
} __attribute__((packed, aligned(8))); } __attribute__((packed, aligned(8)));
struct bch_alloc_v4 {
struct bch_val v;
__u64 journal_seq;
__u32 flags;
__u8 gen;
__u8 oldest_gen;
__u8 data_type;
__u8 stripe_redundancy;
__u32 dirty_sectors;
__u32 cached_sectors;
__u64 io_time[2];
__u32 stripe;
__u32 nr_external_backpointers;
struct bpos backpointers[0];
} __attribute__((packed, aligned(8)));
LE32_BITMASK(BCH_ALLOC_V3_NEED_DISCARD,struct bch_alloc_v3, flags, 0, 1)
LE32_BITMASK(BCH_ALLOC_V3_NEED_INC_GEN,struct bch_alloc_v3, flags, 1, 2)
BITMASK(BCH_ALLOC_V4_NEED_DISCARD, struct bch_alloc_v4, flags, 0, 1)
BITMASK(BCH_ALLOC_V4_NEED_INC_GEN, struct bch_alloc_v4, flags, 1, 2)
BITMASK(BCH_ALLOC_V4_BACKPOINTERS_START,struct bch_alloc_v4, flags, 2, 8)
BITMASK(BCH_ALLOC_V4_NR_BACKPOINTERS, struct bch_alloc_v4, flags, 8, 14)
enum { enum {
#define x(name, _bits) BCH_ALLOC_FIELD_V1_##name, #define x(name, _bits) BCH_ALLOC_FIELD_V1_##name,
BCH_ALLOC_FIELDS_V1() BCH_ALLOC_FIELDS_V1()
......
...@@ -149,7 +149,8 @@ static unsigned bch2_key_types_allowed[] = { ...@@ -149,7 +149,8 @@ static unsigned bch2_key_types_allowed[] = {
(1U << KEY_TYPE_deleted)| (1U << KEY_TYPE_deleted)|
(1U << KEY_TYPE_alloc)| (1U << KEY_TYPE_alloc)|
(1U << KEY_TYPE_alloc_v2)| (1U << KEY_TYPE_alloc_v2)|
(1U << KEY_TYPE_alloc_v3), (1U << KEY_TYPE_alloc_v3)|
(1U << KEY_TYPE_alloc_v4),
[BKEY_TYPE_quotas] = [BKEY_TYPE_quotas] =
(1U << KEY_TYPE_deleted)| (1U << KEY_TYPE_deleted)|
(1U << KEY_TYPE_quota), (1U << KEY_TYPE_quota),
......
...@@ -1306,6 +1306,19 @@ static int bch2_gc_start(struct bch_fs *c, ...@@ -1306,6 +1306,19 @@ static int bch2_gc_start(struct bch_fs *c,
return 0; return 0;
} }
/* returns true if not equal */
static inline bool bch2_alloc_v4_cmp(struct bch_alloc_v4 l,
struct bch_alloc_v4 r)
{
return l.gen != r.gen ||
l.oldest_gen != r.oldest_gen ||
l.data_type != r.data_type ||
l.dirty_sectors != r.dirty_sectors ||
l.cached_sectors != r.cached_sectors ||
l.stripe_redundancy != r.stripe_redundancy ||
l.stripe != r.stripe;
}
static int bch2_alloc_write_key(struct btree_trans *trans, static int bch2_alloc_write_key(struct btree_trans *trans,
struct btree_iter *iter, struct btree_iter *iter,
bool metadata_only) bool metadata_only)
...@@ -1314,8 +1327,8 @@ static int bch2_alloc_write_key(struct btree_trans *trans, ...@@ -1314,8 +1327,8 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
struct bch_dev *ca = bch_dev_bkey_exists(c, iter->pos.inode); struct bch_dev *ca = bch_dev_bkey_exists(c, iter->pos.inode);
struct bucket *g; struct bucket *g;
struct bkey_s_c k; struct bkey_s_c k;
struct bkey_alloc_unpacked old_u, new_u, gc_u; struct bkey_i_alloc_v4 *a;
struct bkey_alloc_buf *a; struct bch_alloc_v4 old, new, gc;
int ret; int ret;
k = bch2_btree_iter_peek_slot(iter); k = bch2_btree_iter_peek_slot(iter);
...@@ -1323,60 +1336,61 @@ static int bch2_alloc_write_key(struct btree_trans *trans, ...@@ -1323,60 +1336,61 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
if (ret) if (ret)
return ret; return ret;
old_u = new_u = bch2_alloc_unpack(k); bch2_alloc_to_v4(k, &old);
new = old;
percpu_down_read(&c->mark_lock); percpu_down_read(&c->mark_lock);
g = gc_bucket(ca, iter->pos.offset); g = gc_bucket(ca, iter->pos.offset);
gc_u = (struct bkey_alloc_unpacked) { gc = (struct bch_alloc_v4) {
.dev = iter->pos.inode,
.bucket = iter->pos.offset,
.gen = g->mark.gen, .gen = g->mark.gen,
.data_type = g->mark.data_type, .data_type = g->mark.data_type,
.dirty_sectors = g->mark.dirty_sectors, .dirty_sectors = g->mark.dirty_sectors,
.cached_sectors = g->mark.cached_sectors, .cached_sectors = g->mark.cached_sectors,
.read_time = g->io_time[READ], .io_time[READ] = g->io_time[READ],
.write_time = g->io_time[WRITE], .io_time[WRITE] = g->io_time[WRITE],
.stripe = g->stripe, .stripe = g->stripe,
.stripe_redundancy = g->stripe_redundancy, .stripe_redundancy = g->stripe_redundancy,
}; };
percpu_up_read(&c->mark_lock); percpu_up_read(&c->mark_lock);
if (metadata_only && if (metadata_only &&
gc_u.data_type != BCH_DATA_sb && gc.data_type != BCH_DATA_sb &&
gc_u.data_type != BCH_DATA_journal && gc.data_type != BCH_DATA_journal &&
gc_u.data_type != BCH_DATA_btree) gc.data_type != BCH_DATA_btree)
return 0; return 0;
if (gen_after(old_u.gen, gc_u.gen)) if (gen_after(old.gen, gc.gen))
return 0; return 0;
#define copy_bucket_field(_f) \ #define copy_bucket_field(_f) \
if (fsck_err_on(new_u._f != gc_u._f, c, \ if (fsck_err_on(new._f != gc._f, c, \
"bucket %llu:%llu gen %u data type %s has wrong " #_f \ "bucket %llu:%llu gen %u data type %s has wrong " #_f \
": got %u, should be %u", \ ": got %u, should be %u", \
iter->pos.inode, iter->pos.offset, \ iter->pos.inode, iter->pos.offset, \
new_u.gen, \ new.gen, \
bch2_data_types[new_u.data_type], \ bch2_data_types[new.data_type], \
new_u._f, gc_u._f)) \ new._f, gc._f)) \
new_u._f = gc_u._f; \ new._f = gc._f; \
copy_bucket_field(gen); copy_bucket_field(gen);
copy_bucket_field(data_type); copy_bucket_field(data_type);
copy_bucket_field(stripe);
copy_bucket_field(dirty_sectors); copy_bucket_field(dirty_sectors);
copy_bucket_field(cached_sectors); copy_bucket_field(cached_sectors);
copy_bucket_field(stripe_redundancy); copy_bucket_field(stripe_redundancy);
copy_bucket_field(stripe); copy_bucket_field(stripe);
#undef copy_bucket_field #undef copy_bucket_field
if (!bkey_alloc_unpacked_cmp(old_u, new_u)) if (!bch2_alloc_v4_cmp(old, new))
return 0; return 0;
a = bch2_alloc_pack(trans, new_u); a = bch2_alloc_to_v4_mut(trans, k);
if (IS_ERR(a)) ret = PTR_ERR_OR_ZERO(a);
return PTR_ERR(a); if (ret)
return ret;
a->v = new;
ret = bch2_trans_update(trans, iter, &a->k, BTREE_TRIGGER_NORUN); ret = bch2_trans_update(trans, iter, &a->k_i, BTREE_TRIGGER_NORUN);
fsck_err: fsck_err:
return ret; return ret;
} }
...@@ -1873,7 +1887,8 @@ static int bch2_alloc_write_oldest_gen(struct btree_trans *trans, struct btree_i ...@@ -1873,7 +1887,8 @@ static int bch2_alloc_write_oldest_gen(struct btree_trans *trans, struct btree_i
{ {
struct bch_dev *ca = bch_dev_bkey_exists(trans->c, iter->pos.inode); struct bch_dev *ca = bch_dev_bkey_exists(trans->c, iter->pos.inode);
struct bkey_s_c k; struct bkey_s_c k;
struct bkey_alloc_unpacked u; struct bch_alloc_v4 a;
struct bkey_i_alloc_v4 *a_mut;
int ret; int ret;
k = bch2_btree_iter_peek_slot(iter); k = bch2_btree_iter_peek_slot(iter);
...@@ -1881,14 +1896,19 @@ static int bch2_alloc_write_oldest_gen(struct btree_trans *trans, struct btree_i ...@@ -1881,14 +1896,19 @@ static int bch2_alloc_write_oldest_gen(struct btree_trans *trans, struct btree_i
if (ret) if (ret)
return ret; return ret;
u = bch2_alloc_unpack(k); bch2_alloc_to_v4(k, &a);
if (u.oldest_gen == ca->oldest_gen[iter->pos.offset]) if (a.oldest_gen == ca->oldest_gen[iter->pos.offset])
return 0; return 0;
u.oldest_gen = ca->oldest_gen[iter->pos.offset]; a_mut = bch2_alloc_to_v4_mut(trans, k);
ret = PTR_ERR_OR_ZERO(a_mut);
if (ret)
return ret;
a_mut->v.oldest_gen = ca->oldest_gen[iter->pos.offset];
return bch2_alloc_write(trans, iter, &u, BTREE_TRIGGER_NORUN); return bch2_trans_update(trans, iter, &a_mut->k_i, 0);
} }
int bch2_gc_gens(struct bch_fs *c) int bch2_gc_gens(struct bch_fs *c)
......
...@@ -678,6 +678,7 @@ enum btree_update_flags { ...@@ -678,6 +678,7 @@ enum btree_update_flags {
((1U << KEY_TYPE_alloc)| \ ((1U << KEY_TYPE_alloc)| \
(1U << KEY_TYPE_alloc_v2)| \ (1U << KEY_TYPE_alloc_v2)| \
(1U << KEY_TYPE_alloc_v3)| \ (1U << KEY_TYPE_alloc_v3)| \
(1U << KEY_TYPE_alloc_v4)| \
(1U << KEY_TYPE_stripe)| \ (1U << KEY_TYPE_stripe)| \
(1U << KEY_TYPE_inode)| \ (1U << KEY_TYPE_inode)| \
(1U << KEY_TYPE_inode_v2)| \ (1U << KEY_TYPE_inode_v2)| \
......
This diff is collapsed.
...@@ -97,6 +97,14 @@ static inline size_t PTR_BUCKET_NR(const struct bch_dev *ca, ...@@ -97,6 +97,14 @@ static inline size_t PTR_BUCKET_NR(const struct bch_dev *ca,
return sector_to_bucket(ca, ptr->offset); return sector_to_bucket(ca, ptr->offset);
} }
static inline struct bpos PTR_BUCKET_POS(const struct bch_fs *c,
const struct bch_extent_ptr *ptr)
{
struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
return POS(ptr->dev, PTR_BUCKET_NR(ca, ptr));
}
static inline struct bucket *PTR_GC_BUCKET(struct bch_dev *ca, static inline struct bucket *PTR_GC_BUCKET(struct bch_dev *ca,
const struct bch_extent_ptr *ptr) const struct bch_extent_ptr *ptr)
{ {
......
...@@ -111,7 +111,7 @@ struct copygc_heap_entry { ...@@ -111,7 +111,7 @@ struct copygc_heap_entry {
u8 dev; u8 dev;
u8 gen; u8 gen;
u8 replicas; u8 replicas;
u16 fragmentation; u32 fragmentation;
u32 sectors; u32 sectors;
u64 offset; u64 offset;
}; };
......
...@@ -129,7 +129,7 @@ static int walk_buckets_to_copygc(struct bch_fs *c) ...@@ -129,7 +129,7 @@ static int walk_buckets_to_copygc(struct bch_fs *c)
struct btree_trans trans; struct btree_trans trans;
struct btree_iter iter; struct btree_iter iter;
struct bkey_s_c k; struct bkey_s_c k;
struct bkey_alloc_unpacked u; struct bch_alloc_v4 a;
int ret; int ret;
bch2_trans_init(&trans, c, 0, 0); bch2_trans_init(&trans, c, 0, 0);
...@@ -139,20 +139,20 @@ static int walk_buckets_to_copygc(struct bch_fs *c) ...@@ -139,20 +139,20 @@ static int walk_buckets_to_copygc(struct bch_fs *c)
struct bch_dev *ca = bch_dev_bkey_exists(c, iter.pos.inode); struct bch_dev *ca = bch_dev_bkey_exists(c, iter.pos.inode);
struct copygc_heap_entry e; struct copygc_heap_entry e;
u = bch2_alloc_unpack(k); bch2_alloc_to_v4(k, &a);
if (u.data_type != BCH_DATA_user || if (a.data_type != BCH_DATA_user ||
u.dirty_sectors >= ca->mi.bucket_size || a.dirty_sectors >= ca->mi.bucket_size ||
bch2_bucket_is_open(c, iter.pos.inode, iter.pos.offset)) bch2_bucket_is_open(c, iter.pos.inode, iter.pos.offset))
continue; continue;
e = (struct copygc_heap_entry) { e = (struct copygc_heap_entry) {
.dev = iter.pos.inode, .dev = iter.pos.inode,
.gen = u.gen, .gen = a.gen,
.replicas = 1 + u.stripe_redundancy, .replicas = 1 + a.stripe_redundancy,
.fragmentation = u.dirty_sectors * (1U << 15) .fragmentation = div_u64((u64) a.dirty_sectors * (1ULL << 31),
/ ca->mi.bucket_size, ca->mi.bucket_size),
.sectors = u.dirty_sectors, .sectors = a.dirty_sectors,
.offset = bucket_to_sector(ca, iter.pos.offset), .offset = bucket_to_sector(ca, iter.pos.offset),
}; };
heap_add_or_replace(h, e, -fragmentation_cmp, NULL); heap_add_or_replace(h, e, -fragmentation_cmp, NULL);
...@@ -180,7 +180,7 @@ static int check_copygc_was_done(struct bch_fs *c, ...@@ -180,7 +180,7 @@ static int check_copygc_was_done(struct bch_fs *c,
struct btree_trans trans; struct btree_trans trans;
struct btree_iter iter; struct btree_iter iter;
struct bkey_s_c k; struct bkey_s_c k;
struct bkey_alloc_unpacked u; struct bch_alloc_v4 a;
struct copygc_heap_entry *i; struct copygc_heap_entry *i;
int ret = 0; int ret = 0;
...@@ -199,10 +199,10 @@ static int check_copygc_was_done(struct bch_fs *c, ...@@ -199,10 +199,10 @@ static int check_copygc_was_done(struct bch_fs *c,
if (ret) if (ret)
break; break;
u = bch2_alloc_unpack(k); bch2_alloc_to_v4(k, &a);
if (u.gen == i->gen && u.dirty_sectors) { if (a.gen == i->gen && a.dirty_sectors) {
*sectors_not_moved += u.dirty_sectors; *sectors_not_moved += a.dirty_sectors;
*buckets_not_moved += 1; *buckets_not_moved += 1;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment