summaryrefslogtreecommitdiff
path: root/libbcachefs/super-io.h
blob: 3811de72c7a9b633178e9f75e89d546d69ee206c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#ifndef _BCACHEFS_SUPER_IO_H
#define _BCACHEFS_SUPER_IO_H

#include "extents.h"
#include "eytzinger.h"
#include "super_types.h"
#include "super.h"

#include <asm/byteorder.h>

struct bch_sb_field *bch2_sb_field_get(struct bch_sb *, enum bch_sb_field_type);
struct bch_sb_field *bch2_sb_field_resize(struct bch_sb_handle *,
					  enum bch_sb_field_type, unsigned);
struct bch_sb_field *bch2_fs_sb_field_resize(struct bch_fs *,
					 enum bch_sb_field_type, unsigned);

#define field_to_type(_f, _name)					\
	container_of_or_null(_f, struct bch_sb_field_##_name, field)

#define x(_name, _nr)							\
static inline struct bch_sb_field_##_name *				\
bch2_sb_get_##_name(struct bch_sb *sb)					\
{									\
	return field_to_type(bch2_sb_field_get(sb,			\
				BCH_SB_FIELD_##_name), _name);		\
}									\
									\
static inline struct bch_sb_field_##_name *				\
bch2_sb_resize_##_name(struct bch_sb_handle *sb, unsigned u64s)	\
{									\
	return field_to_type(bch2_sb_field_resize(sb,			\
				BCH_SB_FIELD_##_name, u64s), _name);	\
}									\
									\
static inline struct bch_sb_field_##_name *				\
bch2_fs_sb_resize_##_name(struct bch_fs *c, unsigned u64s)		\
{									\
	return field_to_type(bch2_fs_sb_field_resize(c,			\
				BCH_SB_FIELD_##_name, u64s), _name);	\
}

BCH_SB_FIELDS()
#undef x

extern const char * const bch2_sb_fields[];

static inline bool bch2_sb_test_feature(struct bch_sb *sb,
					enum bch_sb_features f)
{
	unsigned w = f / 64;
	unsigned b = f % 64;

	return le64_to_cpu(sb->features[w]) & (1ULL << b);
}

static inline void bch2_sb_set_feature(struct bch_sb *sb,
				       enum bch_sb_features f)
{
	if (!bch2_sb_test_feature(sb, f)) {
		unsigned w = f / 64;
		unsigned b = f % 64;

		le64_add_cpu(&sb->features[w], 1ULL << b);
	}
}

static inline __le64 bch2_sb_magic(struct bch_fs *c)
{
	__le64 ret;
	memcpy(&ret, &c->sb.uuid, sizeof(ret));
	return ret;
}

static inline __u64 jset_magic(struct bch_fs *c)
{
	return __le64_to_cpu(bch2_sb_magic(c) ^ JSET_MAGIC);
}

static inline __u64 pset_magic(struct bch_fs *c)
{
	return __le64_to_cpu(bch2_sb_magic(c) ^ PSET_MAGIC);
}

static inline __u64 bset_magic(struct bch_fs *c)
{
	return __le64_to_cpu(bch2_sb_magic(c) ^ BSET_MAGIC);
}

int bch2_sb_to_fs(struct bch_fs *, struct bch_sb *);
int bch2_sb_from_fs(struct bch_fs *, struct bch_dev *);

void bch2_free_super(struct bch_sb_handle *);
int bch2_super_realloc(struct bch_sb_handle *, unsigned);

const char *bch2_sb_validate(struct bch_sb_handle *);

int bch2_read_super(const char *, struct bch_opts *, struct bch_sb_handle *);
void bch2_write_super(struct bch_fs *);

/* BCH_SB_FIELD_journal: */

static inline unsigned bch2_nr_journal_buckets(struct bch_sb_field_journal *j)
{
	return j
		? (__le64 *) vstruct_end(&j->field) - j->buckets
		: 0;
}

/* BCH_SB_FIELD_members: */

static inline bool bch2_member_exists(struct bch_member *m)
{
	return !bch2_is_zero(m->uuid.b, sizeof(uuid_le));
}

static inline bool bch2_dev_exists(struct bch_sb *sb,
				   struct bch_sb_field_members *mi,
				   unsigned dev)
{
	return dev < sb->nr_devices &&
		bch2_member_exists(&mi->members[dev]);
}

static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
{
	return (struct bch_member_cpu) {
		.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),
		.replacement	= BCH_MEMBER_REPLACEMENT(mi),
		.discard	= BCH_MEMBER_DISCARD(mi),
		.data_allowed	= BCH_MEMBER_DATA_ALLOWED(mi),
		.valid		= !bch2_is_zero(mi->uuid.b, sizeof(uuid_le)),
	};
}

/* BCH_SB_FIELD_replicas: */

bool bch2_replicas_marked(struct bch_fs *, enum bch_data_type,
			  struct bch_devs_list);
bool bch2_bkey_replicas_marked(struct bch_fs *, enum bch_data_type,
			       struct bkey_s_c);
int bch2_mark_replicas(struct bch_fs *, enum bch_data_type,
		       struct bch_devs_list);
int bch2_mark_bkey_replicas(struct bch_fs *, enum bch_data_type,
			    struct bkey_s_c);

int bch2_cpu_replicas_to_text(struct bch_replicas_cpu *, char *, size_t);
int bch2_sb_replicas_to_text(struct bch_sb_field_replicas *, char *, size_t);

struct replicas_status {
	struct {
		unsigned	nr_online;
		unsigned	nr_offline;
	}			replicas[BCH_DATA_NR];
};

struct replicas_status __bch2_replicas_status(struct bch_fs *,
					      struct bch_devs_mask);
struct replicas_status bch2_replicas_status(struct bch_fs *);
bool bch2_have_enough_devs(struct replicas_status, unsigned);

unsigned bch2_replicas_online(struct bch_fs *, bool);
unsigned bch2_dev_has_data(struct bch_fs *, struct bch_dev *);

int bch2_replicas_gc_end(struct bch_fs *, int);
int bch2_replicas_gc_start(struct bch_fs *, unsigned);

/* iterate over superblock replicas - used by userspace tools: */

static inline struct bch_replicas_entry *
replicas_entry_next(struct bch_replicas_entry *i)
{
	return (void *) i + offsetof(struct bch_replicas_entry, devs) + i->nr;
}

#define for_each_replicas_entry(_r, _i)					\
	for (_i = (_r)->entries;					\
	     (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;
	};
};

#define TARGET_DEV_START	1
#define TARGET_GROUP_START	(256 + TARGET_DEV_START)

static inline u16 dev_to_target(unsigned dev)
{
	return TARGET_DEV_START + dev;
}

static inline u16 group_to_target(unsigned group)
{
	return TARGET_GROUP_START + group;
}

static inline struct target target_decode(unsigned target)
{
	if (target >= TARGET_GROUP_START)
		return (struct target) {
			.type	= TARGET_GROUP,
			.group	= target - TARGET_GROUP_START
		};

	if (target >= TARGET_DEV_START)
		return (struct target) {
			.type	= TARGET_DEV,
			.group	= target - TARGET_DEV_START
		};

	return (struct target) { .type = TARGET_NULL };
}

static inline bool dev_in_target(struct bch_dev *ca, unsigned target)
{
	struct target t = target_decode(target);

	switch (t.type) {
	case TARGET_NULL:
		return false;
	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);

int __bch2_disk_group_find(struct bch_sb_field_disk_groups *, const char *);

int bch2_opt_target_parse(struct bch_fs *, const char *, u64 *);
int bch2_opt_target_print(struct bch_fs *, char *, size_t, u64);

#endif /* _BCACHEFS_SUPER_IO_H */