Merge tag 'mm-hotfixes-stable-2025-07-11-16-16' of git://git.kernel.org/pub/scm/linux...
[linux-block.git] / fs / bcachefs / opts.c
... / ...
CommitLineData
1// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/kernel.h>
4#include <linux/fs_parser.h>
5
6#include "bcachefs.h"
7#include "compress.h"
8#include "disk_groups.h"
9#include "error.h"
10#include "movinggc.h"
11#include "opts.h"
12#include "rebalance.h"
13#include "recovery_passes.h"
14#include "super-io.h"
15#include "util.h"
16
17#define x(t, n, ...) [n] = #t,
18
19const char * const bch2_error_actions[] = {
20 BCH_ERROR_ACTIONS()
21 NULL
22};
23
24const char * const bch2_degraded_actions[] = {
25 BCH_DEGRADED_ACTIONS()
26 NULL
27};
28
29const char * const bch2_fsck_fix_opts[] = {
30 BCH_FIX_ERRORS_OPTS()
31 NULL
32};
33
34const char * const bch2_version_upgrade_opts[] = {
35 BCH_VERSION_UPGRADE_OPTS()
36 NULL
37};
38
39const char * const bch2_sb_features[] = {
40 BCH_SB_FEATURES()
41 NULL
42};
43
44const char * const bch2_sb_compat[] = {
45 BCH_SB_COMPAT()
46 NULL
47};
48
49const char * const __bch2_btree_ids[] = {
50 BCH_BTREE_IDS()
51 NULL
52};
53
54const char * const __bch2_csum_types[] = {
55 BCH_CSUM_TYPES()
56 NULL
57};
58
59const char * const __bch2_csum_opts[] = {
60 BCH_CSUM_OPTS()
61 NULL
62};
63
64const char * const __bch2_compression_types[] = {
65 BCH_COMPRESSION_TYPES()
66 NULL
67};
68
69const char * const bch2_compression_opts[] = {
70 BCH_COMPRESSION_OPTS()
71 NULL
72};
73
74const char * const __bch2_str_hash_types[] = {
75 BCH_STR_HASH_TYPES()
76 NULL
77};
78
79const char * const bch2_str_hash_opts[] = {
80 BCH_STR_HASH_OPTS()
81 NULL
82};
83
84const char * const __bch2_data_types[] = {
85 BCH_DATA_TYPES()
86 NULL
87};
88
89const char * const bch2_member_states[] = {
90 BCH_MEMBER_STATES()
91 NULL
92};
93
94static const char * const __bch2_jset_entry_types[] = {
95 BCH_JSET_ENTRY_TYPES()
96 NULL
97};
98
99static const char * const __bch2_fs_usage_types[] = {
100 BCH_FS_USAGE_TYPES()
101 NULL
102};
103
104#undef x
105
106static void prt_str_opt_boundscheck(struct printbuf *out, const char * const opts[],
107 unsigned nr, const char *type, unsigned idx)
108{
109 if (idx < nr)
110 prt_str(out, opts[idx]);
111 else
112 prt_printf(out, "(unknown %s %u)", type, idx);
113}
114
115#define PRT_STR_OPT_BOUNDSCHECKED(name, type) \
116void bch2_prt_##name(struct printbuf *out, type t) \
117{ \
118 prt_str_opt_boundscheck(out, __bch2_##name##s, ARRAY_SIZE(__bch2_##name##s) - 1, #name, t);\
119}
120
121PRT_STR_OPT_BOUNDSCHECKED(jset_entry_type, enum bch_jset_entry_type);
122PRT_STR_OPT_BOUNDSCHECKED(fs_usage_type, enum bch_fs_usage_type);
123PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type);
124PRT_STR_OPT_BOUNDSCHECKED(csum_opt, enum bch_csum_opt);
125PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type);
126PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type);
127PRT_STR_OPT_BOUNDSCHECKED(str_hash_type, enum bch_str_hash_type);
128
129static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res,
130 struct printbuf *err)
131{
132 if (!val) {
133 *res = FSCK_FIX_yes;
134 } else {
135 int ret = match_string(bch2_fsck_fix_opts, -1, val);
136
137 if (ret < 0 && err)
138 prt_str(err, "fix_errors: invalid selection");
139 if (ret < 0)
140 return ret;
141 *res = ret;
142 }
143
144 return 0;
145}
146
147static void bch2_opt_fix_errors_to_text(struct printbuf *out,
148 struct bch_fs *c,
149 struct bch_sb *sb,
150 u64 v)
151{
152 prt_str(out, bch2_fsck_fix_opts[v]);
153}
154
155#define bch2_opt_fix_errors (struct bch_opt_fn) { \
156 .parse = bch2_opt_fix_errors_parse, \
157 .to_text = bch2_opt_fix_errors_to_text, \
158}
159
160const char * const bch2_d_types[BCH_DT_MAX] = {
161 [DT_UNKNOWN] = "unknown",
162 [DT_FIFO] = "fifo",
163 [DT_CHR] = "chr",
164 [DT_DIR] = "dir",
165 [DT_BLK] = "blk",
166 [DT_REG] = "reg",
167 [DT_LNK] = "lnk",
168 [DT_SOCK] = "sock",
169 [DT_WHT] = "whiteout",
170 [DT_SUBVOL] = "subvol",
171};
172
173void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src)
174{
175#define x(_name, ...) \
176 if (opt_defined(src, _name)) \
177 opt_set(*dst, _name, src._name);
178
179 BCH_OPTS()
180#undef x
181}
182
183bool bch2_opt_defined_by_id(const struct bch_opts *opts, enum bch_opt_id id)
184{
185 switch (id) {
186#define x(_name, ...) \
187 case Opt_##_name: \
188 return opt_defined(*opts, _name);
189 BCH_OPTS()
190#undef x
191 default:
192 BUG();
193 }
194}
195
196u64 bch2_opt_get_by_id(const struct bch_opts *opts, enum bch_opt_id id)
197{
198 switch (id) {
199#define x(_name, ...) \
200 case Opt_##_name: \
201 return opts->_name;
202 BCH_OPTS()
203#undef x
204 default:
205 BUG();
206 }
207}
208
209void bch2_opt_set_by_id(struct bch_opts *opts, enum bch_opt_id id, u64 v)
210{
211 switch (id) {
212#define x(_name, ...) \
213 case Opt_##_name: \
214 opt_set(*opts, _name, v); \
215 break;
216 BCH_OPTS()
217#undef x
218 default:
219 BUG();
220 }
221}
222
223/* dummy option, for options that aren't stored in the superblock */
224typedef u64 (*sb_opt_get_fn)(const struct bch_sb *);
225typedef void (*sb_opt_set_fn)(struct bch_sb *, u64);
226typedef u64 (*member_opt_get_fn)(const struct bch_member *);
227typedef void (*member_opt_set_fn)(struct bch_member *, u64);
228
229__maybe_unused static const sb_opt_get_fn BCH2_NO_SB_OPT = NULL;
230__maybe_unused static const sb_opt_set_fn SET_BCH2_NO_SB_OPT = NULL;
231__maybe_unused static const member_opt_get_fn BCH2_NO_MEMBER_OPT = NULL;
232__maybe_unused static const member_opt_set_fn SET_BCH2_NO_MEMBER_OPT = NULL;
233
234#define type_compatible_or_null(_p, _type) \
235 __builtin_choose_expr( \
236 __builtin_types_compatible_p(typeof(_p), typeof(_type)), _p, NULL)
237
238const struct bch_option bch2_opt_table[] = {
239#define OPT_BOOL() .type = BCH_OPT_BOOL, .min = 0, .max = 2
240#define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, \
241 .min = _min, .max = _max
242#define OPT_STR(_choices) .type = BCH_OPT_STR, \
243 .min = 0, .max = ARRAY_SIZE(_choices) - 1, \
244 .choices = _choices
245#define OPT_STR_NOLIMIT(_choices) .type = BCH_OPT_STR, \
246 .min = 0, .max = U64_MAX, \
247 .choices = _choices
248#define OPT_BITFIELD(_choices) .type = BCH_OPT_BITFIELD, \
249 .choices = _choices
250#define OPT_FN(_fn) .type = BCH_OPT_FN, .fn = _fn
251
252#define x(_name, _bits, _flags, _type, _sb_opt, _default, _hint, _help) \
253 [Opt_##_name] = { \
254 .attr.name = #_name, \
255 .attr.mode = (_flags) & OPT_RUNTIME ? 0644 : 0444, \
256 .flags = _flags, \
257 .hint = _hint, \
258 .help = _help, \
259 .get_sb = type_compatible_or_null(_sb_opt, *BCH2_NO_SB_OPT), \
260 .set_sb = type_compatible_or_null(SET_##_sb_opt,*SET_BCH2_NO_SB_OPT), \
261 .get_member = type_compatible_or_null(_sb_opt, *BCH2_NO_MEMBER_OPT), \
262 .set_member = type_compatible_or_null(SET_##_sb_opt,*SET_BCH2_NO_MEMBER_OPT),\
263 _type \
264 },
265
266 BCH_OPTS()
267#undef x
268};
269
270int bch2_opt_lookup(const char *name)
271{
272 const struct bch_option *i;
273
274 for (i = bch2_opt_table;
275 i < bch2_opt_table + ARRAY_SIZE(bch2_opt_table);
276 i++)
277 if (!strcmp(name, i->attr.name))
278 return i - bch2_opt_table;
279
280 return -1;
281}
282
283struct opt_synonym {
284 const char *s1, *s2;
285};
286
287static const struct opt_synonym bch2_opt_synonyms[] = {
288 { "quota", "usrquota" },
289};
290
291static int bch2_mount_opt_lookup(const char *name)
292{
293 const struct opt_synonym *i;
294
295 for (i = bch2_opt_synonyms;
296 i < bch2_opt_synonyms + ARRAY_SIZE(bch2_opt_synonyms);
297 i++)
298 if (!strcmp(name, i->s1))
299 name = i->s2;
300
301 return bch2_opt_lookup(name);
302}
303
304struct opt_val_synonym {
305 const char *opt, *v1, *v2;
306};
307
308static const struct opt_val_synonym bch2_opt_val_synonyms[] = {
309 { "degraded", "true", "yes" },
310 { "degraded", "false", "no" },
311 { "degraded", "1", "yes" },
312 { "degraded", "0", "no" },
313};
314
315static const char *bch2_opt_val_synonym_lookup(const char *opt, const char *val)
316{
317 const struct opt_val_synonym *i;
318
319 for (i = bch2_opt_val_synonyms;
320 i < bch2_opt_val_synonyms + ARRAY_SIZE(bch2_opt_val_synonyms);
321 i++)
322 if (!strcmp(opt, i->opt) && !strcmp(val, i->v1))
323 return i->v2;
324
325 return val;
326}
327
328int bch2_opt_validate(const struct bch_option *opt, u64 v, struct printbuf *err)
329{
330 if (v < opt->min) {
331 if (err)
332 prt_printf(err, "%s: too small (min %llu)",
333 opt->attr.name, opt->min);
334 return -BCH_ERR_ERANGE_option_too_small;
335 }
336
337 if (opt->max && v >= opt->max) {
338 if (err)
339 prt_printf(err, "%s: too big (max %llu)",
340 opt->attr.name, opt->max);
341 return -BCH_ERR_ERANGE_option_too_big;
342 }
343
344 if ((opt->flags & OPT_SB_FIELD_SECTORS) && (v & 511)) {
345 if (err)
346 prt_printf(err, "%s: not a multiple of 512",
347 opt->attr.name);
348 return -BCH_ERR_opt_parse_error;
349 }
350
351 if ((opt->flags & OPT_MUST_BE_POW_2) && !is_power_of_2(v)) {
352 if (err)
353 prt_printf(err, "%s: must be a power of two",
354 opt->attr.name);
355 return -BCH_ERR_opt_parse_error;
356 }
357
358 if (opt->fn.validate)
359 return opt->fn.validate(v, err);
360
361 return 0;
362}
363
364int bch2_opt_parse(struct bch_fs *c,
365 const struct bch_option *opt,
366 const char *val, u64 *res,
367 struct printbuf *err)
368{
369 ssize_t ret;
370
371 if (err)
372 printbuf_indent_add_nextline(err, 2);
373
374 switch (opt->type) {
375 case BCH_OPT_BOOL:
376 if (!val)
377 val = "1";
378
379 ret = lookup_constant(bool_names, val, -BCH_ERR_option_not_bool);
380 if (ret != -BCH_ERR_option_not_bool) {
381 *res = ret;
382 } else {
383 if (err)
384 prt_printf(err, "%s: must be bool", opt->attr.name);
385 return ret;
386 }
387 break;
388 case BCH_OPT_UINT:
389 if (!val) {
390 prt_printf(err, "%s: required value",
391 opt->attr.name);
392 return -EINVAL;
393 }
394
395 if (*val != '-') {
396 ret = opt->flags & OPT_HUMAN_READABLE
397 ? bch2_strtou64_h(val, res)
398 : kstrtou64(val, 10, res);
399 } else {
400 prt_printf(err, "%s: must be a non-negative number", opt->attr.name);
401 return -BCH_ERR_option_negative;
402 }
403
404 if (ret < 0) {
405 if (err)
406 prt_printf(err, "%s: must be a number",
407 opt->attr.name);
408 return ret;
409 }
410 break;
411 case BCH_OPT_STR:
412 if (!val) {
413 prt_printf(err, "%s: required value",
414 opt->attr.name);
415 return -EINVAL;
416 }
417
418 ret = match_string(opt->choices, -1, val);
419 if (ret < 0) {
420 if (err)
421 prt_printf(err, "%s: invalid selection",
422 opt->attr.name);
423 return ret;
424 }
425
426 *res = ret;
427 break;
428 case BCH_OPT_BITFIELD: {
429 s64 v = bch2_read_flag_list(val, opt->choices);
430 if (v < 0)
431 return v;
432 *res = v;
433 break;
434 }
435 case BCH_OPT_FN:
436 ret = opt->fn.parse(c, val, res, err);
437
438 if (ret == -BCH_ERR_option_needs_open_fs)
439 return ret;
440
441 if (ret < 0) {
442 if (err)
443 prt_printf(err, "%s: parse error",
444 opt->attr.name);
445 return ret;
446 }
447 }
448
449 return bch2_opt_validate(opt, *res, err);
450}
451
452void bch2_opt_to_text(struct printbuf *out,
453 struct bch_fs *c, struct bch_sb *sb,
454 const struct bch_option *opt, u64 v,
455 unsigned flags)
456{
457 if (flags & OPT_SHOW_MOUNT_STYLE) {
458 if (opt->type == BCH_OPT_BOOL) {
459 prt_printf(out, "%s%s",
460 v ? "" : "no",
461 opt->attr.name);
462 return;
463 }
464
465 prt_printf(out, "%s=", opt->attr.name);
466 }
467
468 switch (opt->type) {
469 case BCH_OPT_BOOL:
470 case BCH_OPT_UINT:
471 if (opt->flags & OPT_HUMAN_READABLE)
472 prt_human_readable_u64(out, v);
473 else
474 prt_printf(out, "%lli", v);
475 break;
476 case BCH_OPT_STR:
477 if (v < opt->min || v >= opt->max)
478 prt_printf(out, "(invalid option %lli)", v);
479 else if (flags & OPT_SHOW_FULL_LIST)
480 prt_string_option(out, opt->choices, v);
481 else
482 prt_str(out, opt->choices[v]);
483 break;
484 case BCH_OPT_BITFIELD:
485 prt_bitflags(out, opt->choices, v);
486 break;
487 case BCH_OPT_FN:
488 opt->fn.to_text(out, c, sb, v);
489 break;
490 default:
491 BUG();
492 }
493}
494
495void bch2_opts_to_text(struct printbuf *out,
496 struct bch_opts opts,
497 struct bch_fs *c, struct bch_sb *sb,
498 unsigned show_mask, unsigned hide_mask,
499 unsigned flags)
500{
501 bool first = true;
502
503 for (enum bch_opt_id i = 0; i < bch2_opts_nr; i++) {
504 const struct bch_option *opt = &bch2_opt_table[i];
505
506 if ((opt->flags & hide_mask) || !(opt->flags & show_mask))
507 continue;
508
509 u64 v = bch2_opt_get_by_id(&opts, i);
510 if (v == bch2_opt_get_by_id(&bch2_opts_default, i))
511 continue;
512
513 if (!first)
514 prt_char(out, ',');
515 first = false;
516
517 bch2_opt_to_text(out, c, sb, opt, v, flags);
518 }
519}
520
521int bch2_opt_hook_pre_set(struct bch_fs *c, struct bch_dev *ca, enum bch_opt_id id, u64 v)
522{
523 int ret = 0;
524
525 switch (id) {
526 case Opt_state:
527 if (ca)
528 return bch2_dev_set_state(c, ca, v, BCH_FORCE_IF_DEGRADED);
529 break;
530
531 case Opt_compression:
532 case Opt_background_compression:
533 ret = bch2_check_set_has_compressed_data(c, v);
534 break;
535 case Opt_erasure_code:
536 if (v)
537 bch2_check_set_feature(c, BCH_FEATURE_ec);
538 break;
539 default:
540 break;
541 }
542
543 return ret;
544}
545
546int bch2_opts_hooks_pre_set(struct bch_fs *c)
547{
548 for (unsigned i = 0; i < bch2_opts_nr; i++) {
549 int ret = bch2_opt_hook_pre_set(c, NULL, i, bch2_opt_get_by_id(&c->opts, i));
550 if (ret)
551 return ret;
552 }
553
554 return 0;
555}
556
557void bch2_opt_hook_post_set(struct bch_fs *c, struct bch_dev *ca, u64 inum,
558 struct bch_opts *new_opts, enum bch_opt_id id)
559{
560 switch (id) {
561 case Opt_foreground_target:
562 if (new_opts->foreground_target &&
563 !new_opts->background_target)
564 bch2_set_rebalance_needs_scan(c, inum);
565 break;
566 case Opt_compression:
567 if (new_opts->compression &&
568 !new_opts->background_compression)
569 bch2_set_rebalance_needs_scan(c, inum);
570 break;
571 case Opt_background_target:
572 if (new_opts->background_target)
573 bch2_set_rebalance_needs_scan(c, inum);
574 break;
575 case Opt_background_compression:
576 if (new_opts->background_compression)
577 bch2_set_rebalance_needs_scan(c, inum);
578 break;
579 case Opt_rebalance_enabled:
580 bch2_rebalance_wakeup(c);
581 break;
582 case Opt_copygc_enabled:
583 bch2_copygc_wakeup(c);
584 break;
585 case Opt_discard:
586 if (!ca) {
587 mutex_lock(&c->sb_lock);
588 for_each_member_device(c, ca) {
589 struct bch_member *m =
590 bch2_members_v2_get_mut(ca->disk_sb.sb, ca->dev_idx);
591 SET_BCH_MEMBER_DISCARD(m, c->opts.discard);
592 }
593
594 bch2_write_super(c);
595 mutex_unlock(&c->sb_lock);
596 }
597 break;
598 case Opt_version_upgrade:
599 /*
600 * XXX: in the future we'll likely want to do compatible
601 * upgrades at runtime as well, but right now there's nothing
602 * that does that:
603 */
604 if (new_opts->version_upgrade == BCH_VERSION_UPGRADE_incompatible)
605 bch2_sb_upgrade_incompat(c);
606 break;
607 default:
608 break;
609 }
610}
611
612int bch2_parse_one_mount_opt(struct bch_fs *c, struct bch_opts *opts,
613 struct printbuf *parse_later,
614 const char *name, const char *val)
615{
616 struct printbuf err = PRINTBUF;
617 u64 v;
618 int ret, id;
619
620 id = bch2_mount_opt_lookup(name);
621
622 /* Check for the form "noopt", negation of a boolean opt: */
623 if (id < 0 &&
624 !val &&
625 !strncmp("no", name, 2)) {
626 id = bch2_mount_opt_lookup(name + 2);
627 val = "0";
628 }
629
630 /* Unknown options are ignored: */
631 if (id < 0)
632 return 0;
633
634 /* must have a value for synonym lookup - but OPT_FN is weird */
635 if (!val && bch2_opt_table[id].type != BCH_OPT_FN)
636 val = "1";
637
638 val = bch2_opt_val_synonym_lookup(name, val);
639
640 if (!(bch2_opt_table[id].flags & OPT_MOUNT))
641 goto bad_opt;
642
643 if (id == Opt_acl &&
644 !IS_ENABLED(CONFIG_BCACHEFS_POSIX_ACL))
645 goto bad_opt;
646
647 if ((id == Opt_usrquota ||
648 id == Opt_grpquota) &&
649 !IS_ENABLED(CONFIG_BCACHEFS_QUOTA))
650 goto bad_opt;
651
652 ret = bch2_opt_parse(c, &bch2_opt_table[id], val, &v, &err);
653 if (ret == -BCH_ERR_option_needs_open_fs) {
654 ret = 0;
655
656 if (parse_later) {
657 prt_printf(parse_later, "%s=%s,", name, val);
658 if (parse_later->allocation_failure)
659 ret = -ENOMEM;
660 }
661
662 goto out;
663 }
664
665 if (ret < 0)
666 goto bad_val;
667
668 if (opts)
669 bch2_opt_set_by_id(opts, id, v);
670
671 ret = 0;
672out:
673 printbuf_exit(&err);
674 return ret;
675bad_opt:
676 ret = -BCH_ERR_option_name;
677 goto out;
678bad_val:
679 ret = -BCH_ERR_option_value;
680 goto out;
681}
682
683int bch2_parse_mount_opts(struct bch_fs *c, struct bch_opts *opts,
684 struct printbuf *parse_later, char *options,
685 bool ignore_unknown)
686{
687 char *copied_opts, *copied_opts_start;
688 char *opt, *name, *val;
689 int ret = 0;
690
691 if (!options)
692 return 0;
693
694 /*
695 * sys_fsconfig() is now occasionally providing us with option lists
696 * starting with a comma - weird.
697 */
698 if (*options == ',')
699 options++;
700
701 copied_opts = kstrdup(options, GFP_KERNEL);
702 if (!copied_opts)
703 return -ENOMEM;
704 copied_opts_start = copied_opts;
705
706 while ((opt = strsep(&copied_opts, ",")) != NULL) {
707 if (!*opt)
708 continue;
709
710 name = strsep(&opt, "=");
711 val = opt;
712
713 ret = bch2_parse_one_mount_opt(c, opts, parse_later, name, val);
714 if (ret == -BCH_ERR_option_name && ignore_unknown)
715 ret = 0;
716 if (ret) {
717 pr_err("Error parsing option %s: %s", name, bch2_err_str(ret));
718 break;
719 }
720 }
721
722 kfree(copied_opts_start);
723 return ret;
724}
725
726u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id, int dev_idx)
727{
728 const struct bch_option *opt = bch2_opt_table + id;
729 u64 v;
730
731 if (dev_idx < 0) {
732 v = opt->get_sb(sb);
733 } else {
734 if (WARN(!bch2_member_exists(sb, dev_idx),
735 "tried to set device option %s on nonexistent device %i",
736 opt->attr.name, dev_idx))
737 return 0;
738
739 struct bch_member m = bch2_sb_member_get(sb, dev_idx);
740 v = opt->get_member(&m);
741 }
742
743 if (opt->flags & OPT_SB_FIELD_ONE_BIAS)
744 --v;
745
746 if (opt->flags & OPT_SB_FIELD_ILOG2)
747 v = 1ULL << v;
748
749 if (opt->flags & OPT_SB_FIELD_SECTORS)
750 v <<= 9;
751
752 return v;
753}
754
755/*
756 * Initial options from superblock - here we don't want any options undefined,
757 * any options the superblock doesn't specify are set to 0:
758 */
759int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb)
760{
761 for (unsigned id = 0; id < bch2_opts_nr; id++) {
762 const struct bch_option *opt = bch2_opt_table + id;
763
764 if (opt->get_sb)
765 bch2_opt_set_by_id(opts, id, bch2_opt_from_sb(sb, id, -1));
766 }
767
768 return 0;
769}
770
771bool __bch2_opt_set_sb(struct bch_sb *sb, int dev_idx,
772 const struct bch_option *opt, u64 v)
773{
774 bool changed = false;
775
776 if (opt->flags & OPT_SB_FIELD_SECTORS)
777 v >>= 9;
778
779 if (opt->flags & OPT_SB_FIELD_ILOG2)
780 v = ilog2(v);
781
782 if (opt->flags & OPT_SB_FIELD_ONE_BIAS)
783 v++;
784
785 if ((opt->flags & OPT_FS) && opt->set_sb && dev_idx < 0) {
786 changed = v != opt->get_sb(sb);
787
788 opt->set_sb(sb, v);
789 }
790
791 if ((opt->flags & OPT_DEVICE) && opt->set_member && dev_idx >= 0) {
792 if (WARN(!bch2_member_exists(sb, dev_idx),
793 "tried to set device option %s on nonexistent device %i",
794 opt->attr.name, dev_idx))
795 return false;
796
797 struct bch_member *m = bch2_members_v2_get_mut(sb, dev_idx);
798 changed = v != opt->get_member(m);
799 opt->set_member(m, v);
800 }
801
802 return changed;
803}
804
805bool bch2_opt_set_sb(struct bch_fs *c, struct bch_dev *ca,
806 const struct bch_option *opt, u64 v)
807{
808 mutex_lock(&c->sb_lock);
809 bool changed = __bch2_opt_set_sb(c->disk_sb.sb, ca ? ca->dev_idx : -1, opt, v);
810 if (changed)
811 bch2_write_super(c);
812 mutex_unlock(&c->sb_lock);
813 return changed;
814}
815
816/* io opts: */
817
818struct bch_io_opts bch2_opts_to_inode_opts(struct bch_opts src)
819{
820 struct bch_io_opts opts = {
821#define x(_name, _bits) ._name = src._name,
822 BCH_INODE_OPTS()
823#undef x
824 };
825
826 bch2_io_opts_fixups(&opts);
827 return opts;
828}
829
830bool bch2_opt_is_inode_opt(enum bch_opt_id id)
831{
832 static const enum bch_opt_id inode_opt_list[] = {
833#define x(_name, _bits) Opt_##_name,
834 BCH_INODE_OPTS()
835#undef x
836 };
837 unsigned i;
838
839 for (i = 0; i < ARRAY_SIZE(inode_opt_list); i++)
840 if (inode_opt_list[i] == id)
841 return true;
842
843 return false;
844}