Commit | Line | Data |
---|---|---|
1c6fdbd8 KO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | #include <linux/kernel.h> | |
4 | ||
5 | #include "bcachefs.h" | |
c258f28e | 6 | #include "compress.h" |
1c6fdbd8 | 7 | #include "disk_groups.h" |
a0f8faea | 8 | #include "error.h" |
1c6fdbd8 | 9 | #include "opts.h" |
13c1e583 | 10 | #include "recovery_passes.h" |
1c6fdbd8 KO |
11 | #include "super-io.h" |
12 | #include "util.h" | |
13 | ||
e8d2fe3b | 14 | #define x(t, n, ...) [n] = #t, |
74b33393 | 15 | |
1c6fdbd8 | 16 | const char * const bch2_error_actions[] = { |
2436cb9f | 17 | BCH_ERROR_ACTIONS() |
1c6fdbd8 KO |
18 | NULL |
19 | }; | |
20 | ||
a0f8faea KO |
21 | const char * const bch2_fsck_fix_opts[] = { |
22 | BCH_FIX_ERRORS_OPTS() | |
23 | NULL | |
24 | }; | |
25 | ||
3045bb95 KO |
26 | const char * const bch2_version_upgrade_opts[] = { |
27 | BCH_VERSION_UPGRADE_OPTS() | |
28 | NULL | |
29 | }; | |
30 | ||
1c3ff72c | 31 | const char * const bch2_sb_features[] = { |
1c3ff72c | 32 | BCH_SB_FEATURES() |
1c3ff72c KO |
33 | NULL |
34 | }; | |
35 | ||
19dd3172 | 36 | const char * const bch2_sb_compat[] = { |
19dd3172 | 37 | BCH_SB_COMPAT() |
19dd3172 KO |
38 | NULL |
39 | }; | |
40 | ||
88dfe193 | 41 | const char * const __bch2_btree_ids[] = { |
41f8b09e | 42 | BCH_BTREE_IDS() |
41f8b09e KO |
43 | NULL |
44 | }; | |
45 | ||
9abb6dd7 | 46 | static const char * const __bch2_csum_types[] = { |
6404dcc9 KO |
47 | BCH_CSUM_TYPES() |
48 | NULL | |
49 | }; | |
50 | ||
1c3ff72c | 51 | const char * const bch2_csum_opts[] = { |
2436cb9f | 52 | BCH_CSUM_OPTS() |
1c6fdbd8 KO |
53 | NULL |
54 | }; | |
55 | ||
9abb6dd7 | 56 | static const char * const __bch2_compression_types[] = { |
6404dcc9 KO |
57 | BCH_COMPRESSION_TYPES() |
58 | NULL | |
59 | }; | |
60 | ||
1c3ff72c | 61 | const char * const bch2_compression_opts[] = { |
1c3ff72c | 62 | BCH_COMPRESSION_OPTS() |
1c6fdbd8 KO |
63 | NULL |
64 | }; | |
65 | ||
66 | const char * const bch2_str_hash_types[] = { | |
6404dcc9 KO |
67 | BCH_STR_HASH_TYPES() |
68 | NULL | |
69 | }; | |
70 | ||
71 | const char * const bch2_str_hash_opts[] = { | |
2436cb9f | 72 | BCH_STR_HASH_OPTS() |
1c6fdbd8 KO |
73 | NULL |
74 | }; | |
75 | ||
e58f963c | 76 | const char * const __bch2_data_types[] = { |
89fd25be | 77 | BCH_DATA_TYPES() |
1c6fdbd8 KO |
78 | NULL |
79 | }; | |
80 | ||
2436cb9f KO |
81 | const char * const bch2_member_states[] = { |
82 | BCH_MEMBER_STATES() | |
1c6fdbd8 KO |
83 | NULL |
84 | }; | |
85 | ||
9abb6dd7 | 86 | static const char * const __bch2_jset_entry_types[] = { |
528b18e6 KO |
87 | BCH_JSET_ENTRY_TYPES() |
88 | NULL | |
89 | }; | |
90 | ||
9abb6dd7 | 91 | static const char * const __bch2_fs_usage_types[] = { |
528b18e6 KO |
92 | BCH_FS_USAGE_TYPES() |
93 | NULL | |
94 | }; | |
95 | ||
2436cb9f | 96 | #undef x |
1c6fdbd8 | 97 | |
9abb6dd7 KO |
98 | static void prt_str_opt_boundscheck(struct printbuf *out, const char * const opts[], |
99 | unsigned nr, const char *type, unsigned idx) | |
100 | { | |
101 | if (idx < nr) | |
102 | prt_str(out, opts[idx]); | |
103 | else | |
104 | prt_printf(out, "(unknown %s %u)", type, idx); | |
105 | } | |
106 | ||
107 | #define PRT_STR_OPT_BOUNDSCHECKED(name, type) \ | |
108 | void bch2_prt_##name(struct printbuf *out, type t) \ | |
109 | { \ | |
110 | prt_str_opt_boundscheck(out, __bch2_##name##s, ARRAY_SIZE(__bch2_##name##s) - 1, #name, t);\ | |
111 | } | |
112 | ||
113 | PRT_STR_OPT_BOUNDSCHECKED(jset_entry_type, enum bch_jset_entry_type); | |
114 | PRT_STR_OPT_BOUNDSCHECKED(fs_usage_type, enum bch_fs_usage_type); | |
115 | PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type); | |
116 | PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type); | |
117 | PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type); | |
118 | ||
a0f8faea KO |
119 | static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res, |
120 | struct printbuf *err) | |
121 | { | |
122 | if (!val) { | |
123 | *res = FSCK_FIX_yes; | |
124 | } else { | |
125 | int ret = match_string(bch2_fsck_fix_opts, -1, val); | |
126 | ||
127 | if (ret < 0 && err) | |
128 | prt_str(err, "fix_errors: invalid selection"); | |
129 | if (ret < 0) | |
130 | return ret; | |
131 | *res = ret; | |
132 | } | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static void bch2_opt_fix_errors_to_text(struct printbuf *out, | |
138 | struct bch_fs *c, | |
139 | struct bch_sb *sb, | |
140 | u64 v) | |
141 | { | |
142 | prt_str(out, bch2_fsck_fix_opts[v]); | |
143 | } | |
144 | ||
bf5a261c KO |
145 | #define bch2_opt_fix_errors (struct bch_opt_fn) { \ |
146 | .parse = bch2_opt_fix_errors_parse, \ | |
147 | .to_text = bch2_opt_fix_errors_to_text, \ | |
148 | } | |
a0f8faea | 149 | |
14b393ee | 150 | const char * const bch2_d_types[BCH_DT_MAX] = { |
d5bee8ca KO |
151 | [DT_UNKNOWN] = "unknown", |
152 | [DT_FIFO] = "fifo", | |
153 | [DT_CHR] = "chr", | |
154 | [DT_DIR] = "dir", | |
155 | [DT_BLK] = "blk", | |
156 | [DT_REG] = "reg", | |
157 | [DT_LNK] = "lnk", | |
158 | [DT_SOCK] = "sock", | |
159 | [DT_WHT] = "whiteout", | |
14b393ee | 160 | [DT_SUBVOL] = "subvol", |
d5bee8ca KO |
161 | }; |
162 | ||
5521b1df KO |
163 | u64 BCH2_NO_SB_OPT(const struct bch_sb *sb) |
164 | { | |
165 | BUG(); | |
166 | } | |
167 | ||
168 | void SET_BCH2_NO_SB_OPT(struct bch_sb *sb, u64 v) | |
169 | { | |
170 | BUG(); | |
171 | } | |
172 | ||
1c6fdbd8 KO |
173 | void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src) |
174 | { | |
0b847a19 | 175 | #define x(_name, ...) \ |
1c6fdbd8 KO |
176 | if (opt_defined(src, _name)) \ |
177 | opt_set(*dst, _name, src._name); | |
178 | ||
179 | BCH_OPTS() | |
0b847a19 | 180 | #undef x |
1c6fdbd8 KO |
181 | } |
182 | ||
183 | bool bch2_opt_defined_by_id(const struct bch_opts *opts, enum bch_opt_id id) | |
184 | { | |
185 | switch (id) { | |
0b847a19 | 186 | #define x(_name, ...) \ |
1c6fdbd8 KO |
187 | case Opt_##_name: \ |
188 | return opt_defined(*opts, _name); | |
189 | BCH_OPTS() | |
0b847a19 | 190 | #undef x |
1c6fdbd8 KO |
191 | default: |
192 | BUG(); | |
193 | } | |
194 | } | |
195 | ||
196 | u64 bch2_opt_get_by_id(const struct bch_opts *opts, enum bch_opt_id id) | |
197 | { | |
198 | switch (id) { | |
0b847a19 | 199 | #define x(_name, ...) \ |
1c6fdbd8 KO |
200 | case Opt_##_name: \ |
201 | return opts->_name; | |
202 | BCH_OPTS() | |
0b847a19 | 203 | #undef x |
1c6fdbd8 KO |
204 | default: |
205 | BUG(); | |
206 | } | |
207 | } | |
208 | ||
209 | void bch2_opt_set_by_id(struct bch_opts *opts, enum bch_opt_id id, u64 v) | |
210 | { | |
211 | switch (id) { | |
0b847a19 | 212 | #define x(_name, ...) \ |
1c6fdbd8 KO |
213 | case Opt_##_name: \ |
214 | opt_set(*opts, _name, v); \ | |
215 | break; | |
216 | BCH_OPTS() | |
0b847a19 | 217 | #undef x |
1c6fdbd8 KO |
218 | default: |
219 | BUG(); | |
220 | } | |
221 | } | |
222 | ||
1c6fdbd8 | 223 | const struct bch_option bch2_opt_table[] = { |
8244f320 KO |
224 | #define OPT_BOOL() .type = BCH_OPT_BOOL, .min = 0, .max = 2 |
225 | #define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, \ | |
226 | .min = _min, .max = _max | |
227 | #define OPT_STR(_choices) .type = BCH_OPT_STR, \ | |
9f343e24 | 228 | .min = 0, .max = ARRAY_SIZE(_choices), \ |
8244f320 | 229 | .choices = _choices |
13c1e583 KO |
230 | #define OPT_STR_NOLIMIT(_choices) .type = BCH_OPT_STR, \ |
231 | .min = 0, .max = U64_MAX, \ | |
232 | .choices = _choices | |
9f343e24 | 233 | #define OPT_FN(_fn) .type = BCH_OPT_FN, .fn = _fn |
1c6fdbd8 | 234 | |
8244f320 | 235 | #define x(_name, _bits, _flags, _type, _sb_opt, _default, _hint, _help) \ |
1c6fdbd8 KO |
236 | [Opt_##_name] = { \ |
237 | .attr = { \ | |
238 | .name = #_name, \ | |
8244f320 | 239 | .mode = (_flags) & OPT_RUNTIME ? 0644 : 0444, \ |
1c6fdbd8 | 240 | }, \ |
8244f320 | 241 | .flags = _flags, \ |
0b847a19 KO |
242 | .hint = _hint, \ |
243 | .help = _help, \ | |
8244f320 | 244 | .get_sb = _sb_opt, \ |
1c6fdbd8 KO |
245 | .set_sb = SET_##_sb_opt, \ |
246 | _type \ | |
247 | }, | |
248 | ||
249 | BCH_OPTS() | |
0b847a19 | 250 | #undef x |
1c6fdbd8 KO |
251 | }; |
252 | ||
253 | int bch2_opt_lookup(const char *name) | |
254 | { | |
255 | const struct bch_option *i; | |
256 | ||
257 | for (i = bch2_opt_table; | |
258 | i < bch2_opt_table + ARRAY_SIZE(bch2_opt_table); | |
259 | i++) | |
260 | if (!strcmp(name, i->attr.name)) | |
261 | return i - bch2_opt_table; | |
262 | ||
263 | return -1; | |
264 | } | |
265 | ||
266 | struct synonym { | |
267 | const char *s1, *s2; | |
268 | }; | |
269 | ||
270 | static const struct synonym bch_opt_synonyms[] = { | |
271 | { "quota", "usrquota" }, | |
272 | }; | |
273 | ||
274 | static int bch2_mount_opt_lookup(const char *name) | |
275 | { | |
276 | const struct synonym *i; | |
277 | ||
278 | for (i = bch_opt_synonyms; | |
279 | i < bch_opt_synonyms + ARRAY_SIZE(bch_opt_synonyms); | |
280 | i++) | |
281 | if (!strcmp(name, i->s1)) | |
282 | name = i->s2; | |
283 | ||
284 | return bch2_opt_lookup(name); | |
285 | } | |
286 | ||
63c4b254 | 287 | int bch2_opt_validate(const struct bch_option *opt, u64 v, struct printbuf *err) |
8244f320 KO |
288 | { |
289 | if (v < opt->min) { | |
63c4b254 | 290 | if (err) |
401ec4db | 291 | prt_printf(err, "%s: too small (min %llu)", |
63c4b254 | 292 | opt->attr.name, opt->min); |
a973de85 | 293 | return -BCH_ERR_ERANGE_option_too_small; |
8244f320 KO |
294 | } |
295 | ||
296 | if (opt->max && v >= opt->max) { | |
63c4b254 | 297 | if (err) |
401ec4db | 298 | prt_printf(err, "%s: too big (max %llu)", |
63c4b254 | 299 | opt->attr.name, opt->max); |
a973de85 | 300 | return -BCH_ERR_ERANGE_option_too_big; |
8244f320 KO |
301 | } |
302 | ||
303 | if ((opt->flags & OPT_SB_FIELD_SECTORS) && (v & 511)) { | |
63c4b254 | 304 | if (err) |
401ec4db | 305 | prt_printf(err, "%s: not a multiple of 512", |
63c4b254 | 306 | opt->attr.name); |
56ec287d | 307 | return -BCH_ERR_opt_parse_error; |
8244f320 KO |
308 | } |
309 | ||
310 | if ((opt->flags & OPT_MUST_BE_POW_2) && !is_power_of_2(v)) { | |
63c4b254 | 311 | if (err) |
401ec4db | 312 | prt_printf(err, "%s: must be a power of two", |
63c4b254 | 313 | opt->attr.name); |
56ec287d | 314 | return -BCH_ERR_opt_parse_error; |
8244f320 KO |
315 | } |
316 | ||
6ddedca2 KO |
317 | if (opt->fn.validate) |
318 | return opt->fn.validate(v, err); | |
319 | ||
8244f320 KO |
320 | return 0; |
321 | } | |
322 | ||
63c4b254 | 323 | int bch2_opt_parse(struct bch_fs *c, |
8244f320 | 324 | const struct bch_option *opt, |
63c4b254 KO |
325 | const char *val, u64 *res, |
326 | struct printbuf *err) | |
1c6fdbd8 KO |
327 | { |
328 | ssize_t ret; | |
329 | ||
330 | switch (opt->type) { | |
331 | case BCH_OPT_BOOL: | |
a0f8faea KO |
332 | if (val) { |
333 | ret = kstrtou64(val, 10, res); | |
334 | } else { | |
335 | ret = 0; | |
336 | *res = 1; | |
337 | } | |
338 | ||
4a7a7ea1 KO |
339 | if (ret < 0 || (*res != 0 && *res != 1)) { |
340 | if (err) | |
a0f8faea | 341 | prt_printf(err, "%s: must be bool", opt->attr.name); |
2a68d611 | 342 | return ret < 0 ? ret : -BCH_ERR_option_not_bool; |
4a7a7ea1 | 343 | } |
1c6fdbd8 KO |
344 | break; |
345 | case BCH_OPT_UINT: | |
a0f8faea KO |
346 | if (!val) { |
347 | prt_printf(err, "%s: required value", | |
348 | opt->attr.name); | |
349 | return -EINVAL; | |
350 | } | |
351 | ||
8244f320 KO |
352 | ret = opt->flags & OPT_HUMAN_READABLE |
353 | ? bch2_strtou64_h(val, res) | |
354 | : kstrtou64(val, 10, res); | |
4a7a7ea1 KO |
355 | if (ret < 0) { |
356 | if (err) | |
401ec4db KO |
357 | prt_printf(err, "%s: must be a number", |
358 | opt->attr.name); | |
1c6fdbd8 | 359 | return ret; |
4a7a7ea1 | 360 | } |
1c6fdbd8 KO |
361 | break; |
362 | case BCH_OPT_STR: | |
a0f8faea KO |
363 | if (!val) { |
364 | prt_printf(err, "%s: required value", | |
365 | opt->attr.name); | |
366 | return -EINVAL; | |
367 | } | |
368 | ||
1c6fdbd8 | 369 | ret = match_string(opt->choices, -1, val); |
4a7a7ea1 KO |
370 | if (ret < 0) { |
371 | if (err) | |
401ec4db KO |
372 | prt_printf(err, "%s: invalid selection", |
373 | opt->attr.name); | |
1c6fdbd8 | 374 | return ret; |
4a7a7ea1 | 375 | } |
1c6fdbd8 KO |
376 | |
377 | *res = ret; | |
378 | break; | |
379 | case BCH_OPT_FN: | |
9f343e24 | 380 | ret = opt->fn.parse(c, val, res, err); |
4a7a7ea1 KO |
381 | if (ret < 0) { |
382 | if (err) | |
401ec4db KO |
383 | prt_printf(err, "%s: parse error", |
384 | opt->attr.name); | |
8244f320 | 385 | return ret; |
4a7a7ea1 | 386 | } |
1c6fdbd8 KO |
387 | } |
388 | ||
63c4b254 | 389 | return bch2_opt_validate(opt, *res, err); |
1c6fdbd8 KO |
390 | } |
391 | ||
5521b1df KO |
392 | void bch2_opt_to_text(struct printbuf *out, |
393 | struct bch_fs *c, struct bch_sb *sb, | |
319f9ac3 KO |
394 | const struct bch_option *opt, u64 v, |
395 | unsigned flags) | |
1c6fdbd8 | 396 | { |
1c6fdbd8 | 397 | if (flags & OPT_SHOW_MOUNT_STYLE) { |
319f9ac3 | 398 | if (opt->type == BCH_OPT_BOOL) { |
401ec4db | 399 | prt_printf(out, "%s%s", |
319f9ac3 KO |
400 | v ? "" : "no", |
401 | opt->attr.name); | |
402 | return; | |
403 | } | |
404 | ||
401ec4db | 405 | prt_printf(out, "%s=", opt->attr.name); |
1c6fdbd8 KO |
406 | } |
407 | ||
408 | switch (opt->type) { | |
409 | case BCH_OPT_BOOL: | |
410 | case BCH_OPT_UINT: | |
8244f320 | 411 | if (opt->flags & OPT_HUMAN_READABLE) |
401ec4db | 412 | prt_human_readable_u64(out, v); |
8244f320 | 413 | else |
401ec4db | 414 | prt_printf(out, "%lli", v); |
0b847a19 | 415 | break; |
1c6fdbd8 | 416 | case BCH_OPT_STR: |
319f9ac3 | 417 | if (flags & OPT_SHOW_FULL_LIST) |
401ec4db | 418 | prt_string_option(out, opt->choices, v); |
319f9ac3 | 419 | else |
a0f8faea | 420 | prt_str(out, opt->choices[v]); |
1c6fdbd8 KO |
421 | break; |
422 | case BCH_OPT_FN: | |
9f343e24 | 423 | opt->fn.to_text(out, c, sb, v); |
319f9ac3 | 424 | break; |
1c6fdbd8 KO |
425 | default: |
426 | BUG(); | |
427 | } | |
1c6fdbd8 KO |
428 | } |
429 | ||
c258f28e KO |
430 | int bch2_opt_check_may_set(struct bch_fs *c, int id, u64 v) |
431 | { | |
432 | int ret = 0; | |
433 | ||
434 | switch (id) { | |
435 | case Opt_compression: | |
436 | case Opt_background_compression: | |
437 | ret = bch2_check_set_has_compressed_data(c, v); | |
438 | break; | |
cd575ddf | 439 | case Opt_erasure_code: |
ba239c95 | 440 | if (v) |
1c3ff72c | 441 | bch2_check_set_feature(c, BCH_FEATURE_ec); |
cd575ddf | 442 | break; |
c258f28e KO |
443 | } |
444 | ||
445 | return ret; | |
446 | } | |
447 | ||
cd575ddf KO |
448 | int bch2_opts_check_may_set(struct bch_fs *c) |
449 | { | |
450 | unsigned i; | |
451 | int ret; | |
452 | ||
453 | for (i = 0; i < bch2_opts_nr; i++) { | |
454 | ret = bch2_opt_check_may_set(c, i, | |
455 | bch2_opt_get_by_id(&c->opts, i)); | |
456 | if (ret) | |
457 | return ret; | |
458 | } | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
a10e677a KO |
463 | int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts, |
464 | char *options) | |
1c6fdbd8 | 465 | { |
baf056b8 | 466 | char *copied_opts, *copied_opts_start; |
1c6fdbd8 KO |
467 | char *opt, *name, *val; |
468 | int ret, id; | |
63c4b254 | 469 | struct printbuf err = PRINTBUF; |
1c6fdbd8 KO |
470 | u64 v; |
471 | ||
baf056b8 DR |
472 | if (!options) |
473 | return 0; | |
474 | ||
cf416e7a KO |
475 | /* |
476 | * sys_fsconfig() is now occasionally providing us with option lists | |
477 | * starting with a comma - weird. | |
478 | */ | |
479 | if (*options == ',') | |
480 | options++; | |
481 | ||
baf056b8 DR |
482 | copied_opts = kstrdup(options, GFP_KERNEL); |
483 | if (!copied_opts) | |
79162e82 | 484 | return -ENOMEM; |
baf056b8 DR |
485 | copied_opts_start = copied_opts; |
486 | ||
487 | while ((opt = strsep(&copied_opts, ",")) != NULL) { | |
1c6fdbd8 KO |
488 | name = strsep(&opt, "="); |
489 | val = opt; | |
490 | ||
a0f8faea | 491 | id = bch2_mount_opt_lookup(name); |
1c6fdbd8 | 492 | |
a0f8faea KO |
493 | /* Check for the form "noopt", negation of a boolean opt: */ |
494 | if (id < 0 && | |
495 | !val && | |
496 | !strncmp("no", name, 2)) { | |
497 | id = bch2_mount_opt_lookup(name + 2); | |
498 | val = "0"; | |
1c6fdbd8 KO |
499 | } |
500 | ||
03ef80b4 | 501 | /* Unknown options are ignored: */ |
a0f8faea | 502 | if (id < 0) |
03ef80b4 | 503 | continue; |
a0f8faea | 504 | |
8244f320 | 505 | if (!(bch2_opt_table[id].flags & OPT_MOUNT)) |
1c6fdbd8 KO |
506 | goto bad_opt; |
507 | ||
508 | if (id == Opt_acl && | |
509 | !IS_ENABLED(CONFIG_BCACHEFS_POSIX_ACL)) | |
510 | goto bad_opt; | |
511 | ||
512 | if ((id == Opt_usrquota || | |
513 | id == Opt_grpquota) && | |
514 | !IS_ENABLED(CONFIG_BCACHEFS_QUOTA)) | |
515 | goto bad_opt; | |
516 | ||
a0f8faea KO |
517 | ret = bch2_opt_parse(c, &bch2_opt_table[id], val, &v, &err); |
518 | if (ret < 0) | |
519 | goto bad_val; | |
520 | ||
1c6fdbd8 KO |
521 | bch2_opt_set_by_id(opts, id, v); |
522 | } | |
523 | ||
baf056b8 DR |
524 | ret = 0; |
525 | goto out; | |
526 | ||
1c6fdbd8 KO |
527 | bad_opt: |
528 | pr_err("Bad mount option %s", name); | |
79162e82 | 529 | ret = -BCH_ERR_option_name; |
baf056b8 | 530 | goto out; |
1c6fdbd8 | 531 | bad_val: |
63c4b254 | 532 | pr_err("Invalid mount option %s", err.buf); |
79162e82 | 533 | ret = -BCH_ERR_option_value; |
baf056b8 | 534 | goto out; |
baf056b8 DR |
535 | out: |
536 | kfree(copied_opts_start); | |
63c4b254 | 537 | printbuf_exit(&err); |
baf056b8 | 538 | return ret; |
1c6fdbd8 KO |
539 | } |
540 | ||
5521b1df KO |
541 | u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id) |
542 | { | |
543 | const struct bch_option *opt = bch2_opt_table + id; | |
544 | u64 v; | |
545 | ||
546 | v = opt->get_sb(sb); | |
547 | ||
548 | if (opt->flags & OPT_SB_FIELD_ILOG2) | |
549 | v = 1ULL << v; | |
550 | ||
551 | if (opt->flags & OPT_SB_FIELD_SECTORS) | |
552 | v <<= 9; | |
553 | ||
554 | return v; | |
555 | } | |
556 | ||
8244f320 KO |
557 | /* |
558 | * Initial options from superblock - here we don't want any options undefined, | |
559 | * any options the superblock doesn't specify are set to 0: | |
560 | */ | |
561 | int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb) | |
562 | { | |
563 | unsigned id; | |
8244f320 KO |
564 | |
565 | for (id = 0; id < bch2_opts_nr; id++) { | |
566 | const struct bch_option *opt = bch2_opt_table + id; | |
8244f320 | 567 | |
5521b1df | 568 | if (opt->get_sb == BCH2_NO_SB_OPT) |
8244f320 KO |
569 | continue; |
570 | ||
63c4b254 | 571 | bch2_opt_set_by_id(opts, id, bch2_opt_from_sb(sb, id)); |
8244f320 KO |
572 | } |
573 | ||
574 | return 0; | |
575 | } | |
576 | ||
577 | void __bch2_opt_set_sb(struct bch_sb *sb, const struct bch_option *opt, u64 v) | |
578 | { | |
5521b1df | 579 | if (opt->set_sb == SET_BCH2_NO_SB_OPT) |
8244f320 KO |
580 | return; |
581 | ||
582 | if (opt->flags & OPT_SB_FIELD_SECTORS) | |
583 | v >>= 9; | |
584 | ||
585 | if (opt->flags & OPT_SB_FIELD_ILOG2) | |
586 | v = ilog2(v); | |
587 | ||
588 | opt->set_sb(sb, v); | |
589 | } | |
590 | ||
591 | void bch2_opt_set_sb(struct bch_fs *c, const struct bch_option *opt, u64 v) | |
592 | { | |
5521b1df | 593 | if (opt->set_sb == SET_BCH2_NO_SB_OPT) |
8244f320 KO |
594 | return; |
595 | ||
596 | mutex_lock(&c->sb_lock); | |
597 | __bch2_opt_set_sb(c->disk_sb.sb, opt, v); | |
598 | bch2_write_super(c); | |
599 | mutex_unlock(&c->sb_lock); | |
600 | } | |
601 | ||
1c6fdbd8 KO |
602 | /* io opts: */ |
603 | ||
604 | struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src) | |
605 | { | |
01ad6737 KO |
606 | return (struct bch_io_opts) { |
607 | #define x(_name, _bits) ._name = src._name, | |
1c6fdbd8 | 608 | BCH_INODE_OPTS() |
a3e70fb2 | 609 | #undef x |
01ad6737 | 610 | }; |
1c6fdbd8 KO |
611 | } |
612 | ||
613 | bool bch2_opt_is_inode_opt(enum bch_opt_id id) | |
614 | { | |
615 | static const enum bch_opt_id inode_opt_list[] = { | |
a3e70fb2 | 616 | #define x(_name, _bits) Opt_##_name, |
1c6fdbd8 | 617 | BCH_INODE_OPTS() |
a3e70fb2 | 618 | #undef x |
1c6fdbd8 KO |
619 | }; |
620 | unsigned i; | |
621 | ||
622 | for (i = 0; i < ARRAY_SIZE(inode_opt_list); i++) | |
623 | if (inode_opt_list[i] == id) | |
624 | return true; | |
625 | ||
626 | return false; | |
627 | } |