summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2017-12-03 19:36:48 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-02-19 19:00:30 -0500
commit90d78c246188f4e90bd9ceb29fe95186b7dc680d (patch)
treeaa99bc6a9c5aa10f8b6a702583a8dde17a115f74
parent2c997de0e437923ceb6b0d2f7c44c7106ccf5545 (diff)
bcachefs: Option parsing for io targets
-rw-r--r--fs/bcachefs/chardev.c14
-rw-r--r--fs/bcachefs/fs.c16
-rw-r--r--fs/bcachefs/opts.c49
-rw-r--r--fs/bcachefs/opts.h22
-rw-r--r--fs/bcachefs/super-io.c138
-rw-r--r--fs/bcachefs/super-io.h5
-rw-r--r--fs/bcachefs/super.c89
-rw-r--r--fs/bcachefs/super.h2
-rw-r--r--fs/bcachefs/sysfs.c34
-rw-r--r--fs/bcachefs/xattr.c10
10 files changed, 331 insertions, 48 deletions
diff --git a/fs/bcachefs/chardev.c b/fs/bcachefs/chardev.c
index 5ff90cc0015f..ab6dc665186e 100644
--- a/fs/bcachefs/chardev.c
+++ b/fs/bcachefs/chardev.c
@@ -40,27 +40,15 @@ static struct bch_dev *bch2_device_lookup(struct bch_fs *c, u64 dev,
if (!ca)
return ERR_PTR(-EINVAL);
} else {
- struct block_device *bdev;
char *path;
- unsigned i;
path = strndup_user((const char __user *)
(unsigned long) dev, PATH_MAX);
if (IS_ERR(path))
return ERR_CAST(path);
- bdev = lookup_bdev(path);
+ ca = bch2_dev_lookup(c, path);
kfree(path);
- if (IS_ERR(bdev))
- return ERR_CAST(bdev);
-
- for_each_member_device(ca, c, i)
- if (ca->disk_sb.bdev == bdev)
- goto found;
-
- ca = ERR_PTR(-ENOENT);
-found:
- bdput(bdev);
}
return ca;
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index 80962b5d16a1..c7e842ee8437 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -1266,6 +1266,7 @@ static int bch2_show_options(struct seq_file *seq, struct dentry *root)
{
struct bch_fs *c = root->d_sb->s_fs_info;
enum bch_opt_id i;
+ char buf[512];
for (i = 0; i < bch2_opts_nr; i++) {
const struct bch_option *opt = &bch2_opt_table[i];
@@ -1277,17 +1278,10 @@ static int bch2_show_options(struct seq_file *seq, struct dentry *root)
if (v == bch2_opt_get_by_id(&bch2_opts_default, i))
continue;
- switch (opt->type) {
- case BCH_OPT_BOOL:
- seq_printf(seq, ",%s%s", v ? "" : "no", opt->attr.name);
- break;
- case BCH_OPT_UINT:
- seq_printf(seq, ",%s=%llu", opt->attr.name, v);
- break;
- case BCH_OPT_STR:
- seq_printf(seq, ",%s=%s", opt->attr.name, opt->choices[v]);
- break;
- }
+ bch2_opt_to_text(c, buf, sizeof(buf), opt, v,
+ OPT_SHOW_MOUNT_STYLE);
+ seq_putc(seq, ',');
+ seq_puts(seq, buf);
}
return 0;
diff --git a/fs/bcachefs/opts.c b/fs/bcachefs/opts.c
index ec50345fda62..326b8ad9caf2 100644
--- a/fs/bcachefs/opts.c
+++ b/fs/bcachefs/opts.c
@@ -1,7 +1,9 @@
#include <linux/kernel.h>
+#include "bcachefs.h"
#include "opts.h"
+#include "super-io.h"
#include "util.h"
const char * const bch2_error_actions[] = {
@@ -139,6 +141,9 @@ const struct bch_option bch2_opt_table[] = {
#define OPT_BOOL() .type = BCH_OPT_BOOL
#define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, .min = _min, .max = _max
#define OPT_STR(_choices) .type = BCH_OPT_STR, .choices = _choices
+#define OPT_FN(_fn) .type = BCH_OPT_FN, \
+ .parse = _fn##_parse, \
+ .print = _fn##_print
#define BCH_OPT(_name, _bits, _mode, _type, _sb_opt, _default) \
[Opt_##_name] = { \
@@ -189,7 +194,8 @@ static int bch2_mount_opt_lookup(const char *name)
return bch2_opt_lookup(name);
}
-int bch2_opt_parse(const struct bch_option *opt, const char *val, u64 *res)
+int bch2_opt_parse(struct bch_fs *c, const struct bch_option *opt,
+ const char *val, u64 *res)
{
ssize_t ret;
@@ -217,11 +223,50 @@ int bch2_opt_parse(const struct bch_option *opt, const char *val, u64 *res)
*res = ret;
break;
+ case BCH_OPT_FN:
+ if (!c)
+ return -EINVAL;
+
+ return opt->parse(c, val, res);
}
return 0;
}
+int bch2_opt_to_text(struct bch_fs *c, char *buf, size_t len,
+ const struct bch_option *opt, u64 v,
+ unsigned flags)
+{
+ char *out = buf, *end = buf + len;
+
+ if (flags & OPT_SHOW_MOUNT_STYLE) {
+ if (opt->type == BCH_OPT_BOOL)
+ return scnprintf(out, end - out, "%s%s",
+ v ? "" : "no",
+ opt->attr.name);
+
+ out += scnprintf(out, end - out, "%s=", opt->attr.name);
+ }
+
+ switch (opt->type) {
+ case BCH_OPT_BOOL:
+ case BCH_OPT_UINT:
+ out += scnprintf(out, end - out, "%lli", v);
+ break;
+ case BCH_OPT_STR:
+ out += (flags & OPT_SHOW_FULL_LIST)
+ ? bch2_scnprint_string_list(out, end - out, opt->choices, v)
+ : scnprintf(out, end - out, opt->choices[v]);
+ break;
+ case BCH_OPT_FN:
+ return opt->print(c, out, end - out, v);
+ default:
+ BUG();
+ }
+
+ return out - buf;
+}
+
int bch2_parse_mount_opts(struct bch_opts *opts, char *options)
{
char *opt, *name, *val;
@@ -237,7 +282,7 @@ int bch2_parse_mount_opts(struct bch_opts *opts, char *options)
if (id < 0)
goto bad_opt;
- ret = bch2_opt_parse(&bch2_opt_table[id], val, &v);
+ ret = bch2_opt_parse(NULL, &bch2_opt_table[id], val, &v);
if (ret < 0)
goto bad_val;
} else {
diff --git a/fs/bcachefs/opts.h b/fs/bcachefs/opts.h
index ec4e36ab8dd8..e7ab8870d3ac 100644
--- a/fs/bcachefs/opts.h
+++ b/fs/bcachefs/opts.h
@@ -42,6 +42,7 @@ enum opt_type {
BCH_OPT_BOOL,
BCH_OPT_UINT,
BCH_OPT_STR,
+ BCH_OPT_FN,
};
/**
@@ -101,13 +102,13 @@ enum opt_type {
OPT_STR(bch2_str_hash_types), \
BCH_SB_STR_HASH_TYPE, BCH_STR_HASH_SIPHASH) \
BCH_OPT(foreground_target, u16, OPT_RUNTIME, \
- OPT_UINT(0, U16_MAX), \
+ OPT_FN(bch2_opt_target), \
BCH_SB_FOREGROUND_TARGET, 0) \
BCH_OPT(background_target, u16, OPT_RUNTIME, \
- OPT_UINT(0, U16_MAX), \
+ OPT_FN(bch2_opt_target), \
BCH_SB_BACKGROUND_TARGET, 0) \
BCH_OPT(promote_target, u16, OPT_RUNTIME, \
- OPT_UINT(0, U16_MAX), \
+ OPT_FN(bch2_opt_target), \
BCH_SB_PROMOTE_TARGET, 0) \
BCH_OPT(inodes_32bit, u8, OPT_RUNTIME, \
OPT_BOOL(), \
@@ -217,6 +218,8 @@ enum bch_opt_id {
bch2_opts_nr
};
+struct bch_fs;
+
struct bch_option {
struct attribute attr;
void (*set_sb)(struct bch_sb *, u64);
@@ -230,6 +233,10 @@ struct bch_option {
struct {
const char * const *choices;
};
+ struct {
+ int (*parse)(struct bch_fs *, const char *, u64 *);
+ int (*print)(struct bch_fs *, char *, size_t, u64);
+ };
};
};
@@ -243,7 +250,14 @@ void bch2_opt_set_by_id(struct bch_opts *, enum bch_opt_id, u64);
struct bch_opts bch2_opts_from_sb(struct bch_sb *);
int bch2_opt_lookup(const char *);
-int bch2_opt_parse(const struct bch_option *, const char *, u64 *);
+int bch2_opt_parse(struct bch_fs *, const struct bch_option *, const char *, u64 *);
+
+#define OPT_SHOW_FULL_LIST (1 << 0)
+#define OPT_SHOW_MOUNT_STYLE (1 << 1)
+
+int bch2_opt_to_text(struct bch_fs *, char *, size_t,
+ const struct bch_option *, u64, unsigned);
+
int bch2_parse_mount_opts(struct bch_opts *, char *);
/* inode opts: */
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index ba32e69d2a98..69101f3a68a5 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -1736,18 +1736,140 @@ const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned targe
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_DEV: {
+ struct bch_dev *ca = t.dev < c->sb.nr_devices
+ ? rcu_dereference(c->devs[t.dev])
+ : NULL;
+ return ca ? &ca->self : NULL;
+ }
+ case TARGET_GROUP: {
+ struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
+
+ return t.group < g->nr && !g->entries[t.group].deleted
+ ? &g->entries[t.group].devs
+ : NULL;
+ }
+ default:
+ BUG();
+ }
+}
+
+int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
+ const char *name)
+{
+ unsigned i, nr_groups = disk_groups_nr(groups);
+ unsigned len = strlen(name);
+
+ for (i = 0; i < nr_groups; i++) {
+ struct bch_disk_group *g = groups->entries + i;
+
+ if (BCH_GROUP_DELETED(g))
+ continue;
+
+ if (strnlen(g->label, sizeof(g->label)) == len &&
+ !memcmp(name, g->label, len))
+ return i;
+ }
+
+ return -1;
+}
+
+static int bch2_disk_group_find(struct bch_fs *c, const char *name)
+{
+ int ret;
+
+ mutex_lock(&c->sb_lock);
+ ret = __bch2_disk_group_find(bch2_sb_get_disk_groups(c->disk_sb), name);
+ mutex_unlock(&c->sb_lock);
+
+ return ret;
+}
+
+int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
+{
+ struct bch_dev *ca;
+ int g;
+
+ if (!strlen(buf) || !strcmp(buf, "none")) {
+ *v = 0;
+ return 0;
+ }
+
+ /* Is it a device? */
+ ca = bch2_dev_lookup(c, buf);
+ if (!IS_ERR(ca)) {
+ *v = dev_to_target(ca->dev_idx);
+ percpu_ref_put(&ca->ref);
+ return 0;
+ }
+
+ g = bch2_disk_group_find(c, buf);
+ if (g >= 0) {
+ *v = group_to_target(g);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int bch2_opt_target_print(struct bch_fs *c, char *buf, size_t len, u64 v)
+{
+ struct target t = target_decode(v);
+ int ret;
+
+ switch (t.type) {
+ case TARGET_NULL:
+ return scnprintf(buf, len, "none");
+ case TARGET_DEV: {
+ struct bch_dev *ca;
+
+ rcu_read_lock();
+ ca = t.dev < c->sb.nr_devices
+ ? rcu_dereference(c->devs[t.dev])
+ : NULL;
+
+ if (ca && percpu_ref_tryget(&ca->io_ref)) {
+ char b[BDEVNAME_SIZE];
+
+ ret = scnprintf(buf, len, "/dev/%s",
+ bdevname(ca->disk_sb.bdev, b));
+ percpu_ref_put(&ca->io_ref);
+ } else if (ca) {
+ ret = scnprintf(buf, len, "offline device %u", t.dev);
+ } else {
+ ret = scnprintf(buf, len, "invalid device %u", t.dev);
+ }
+
+ rcu_read_unlock();
+ break;
+ }
case TARGET_GROUP: {
- struct bch_disk_groups_cpu *g =
- rcu_dereference(c->disk_groups);
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_disk_group *g;
+
+ mutex_lock(&c->sb_lock);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
- /* XXX: what to do here? */
- BUG_ON(t.group >= g->nr || g->entries[t.group].deleted);
- return &g->entries[t.group].devs;
+ g = t.group < disk_groups_nr(groups)
+ ? groups->entries + t.group
+ : NULL;
+
+ if (g && !BCH_GROUP_DELETED(g)) {
+ ret = len ? min(len - 1, strnlen(g->label, sizeof(g->label))) : 0;
+
+ memcpy(buf, g->label, ret);
+ if (len)
+ buf[ret] = '\0';
+ } else {
+ ret = scnprintf(buf, len, "invalid group %u", t.group);
+ }
+
+ mutex_unlock(&c->sb_lock);
+ break;
}
default:
BUG();
}
+
+ return ret;
}
diff --git a/fs/bcachefs/super-io.h b/fs/bcachefs/super-io.h
index cd8bfe6cfea8..3811de72c7a9 100644
--- a/fs/bcachefs/super-io.h
+++ b/fs/bcachefs/super-io.h
@@ -251,4 +251,9 @@ static inline bool dev_in_target(struct bch_dev *ca, unsigned target)
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 */
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 287535e9c4f7..abb971286cdd 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1702,6 +1702,95 @@ err:
return ret;
}
+/* return with ref on ca->ref: */
+struct bch_dev *bch2_dev_lookup(struct bch_fs *c, const char *path)
+{
+
+ struct block_device *bdev = lookup_bdev(path);
+ struct bch_dev *ca;
+ unsigned i;
+
+ if (IS_ERR(bdev))
+ return ERR_CAST(bdev);
+
+ for_each_member_device(ca, c, i)
+ if (ca->disk_sb.bdev == bdev)
+ goto found;
+
+ ca = ERR_PTR(-ENOENT);
+found:
+ bdput(bdev);
+ return ca;
+}
+
+int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *label)
+{
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_disk_group *g;
+ struct bch_member *mi;
+ unsigned i, v, nr_groups;
+ int ret;
+
+ if (strlen(label) > BCH_SB_LABEL_SIZE)
+ return -EINVAL;
+
+ mutex_lock(&c->sb_lock);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+ nr_groups = disk_groups_nr(groups);
+
+ if (!strcmp(label, "none")) {
+ v = 0;
+ goto write_sb;
+ }
+
+ ret = __bch2_disk_group_find(groups, label);
+ if (ret >= 0) {
+ v = ret + 1;
+ goto write_sb;
+ }
+
+ /* not found - create a new disk group: */
+
+ for (i = 0;
+ i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
+ i++)
+ ;
+
+ if (i == nr_groups) {
+ unsigned u64s =
+ (sizeof(struct bch_sb_field_disk_groups) +
+ sizeof(struct bch_disk_group) * (nr_groups + 1)) /
+ sizeof(u64);
+
+ groups = bch2_fs_sb_resize_disk_groups(c, u64s);
+ if (!groups) {
+ mutex_unlock(&c->sb_lock);
+ return -ENOSPC;
+ }
+
+ nr_groups = disk_groups_nr(groups);
+ }
+
+ BUG_ON(i >= nr_groups);
+
+ g = &groups->entries[i];
+ v = i + 1;
+
+ memcpy(g->label, label, strlen(label));
+ if (strlen(label) < sizeof(g->label))
+ g->label[strlen(label)] = '\0';
+ SET_BCH_GROUP_DELETED(g, 0);
+ SET_BCH_GROUP_DATA_ALLOWED(g, ~0);
+write_sb:
+ mi = &bch2_sb_get_members(c->disk_sb)->members[ca->dev_idx];
+ SET_BCH_MEMBER_GROUP(mi, v);
+
+ bch2_write_super(c);
+ mutex_unlock(&c->sb_lock);
+
+ return 0;
+}
+
/* Filesystem open: */
struct bch_fs *bch2_fs_open(char * const *devices, unsigned nr_devices,
diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h
index 1718f5c10303..652a572ff329 100644
--- a/fs/bcachefs/super.h
+++ b/fs/bcachefs/super.h
@@ -194,6 +194,8 @@ int bch2_dev_add(struct bch_fs *, const char *);
int bch2_dev_online(struct bch_fs *, const char *);
int bch2_dev_offline(struct bch_fs *, struct bch_dev *, int);
int bch2_dev_resize(struct bch_fs *, struct bch_dev *, u64);
+struct bch_dev *bch2_dev_lookup(struct bch_fs *, const char *);
+int bch2_dev_group_set(struct bch_fs *, struct bch_dev *, const char *);
bool bch2_fs_emergency_read_only(struct bch_fs *);
void bch2_fs_read_only(struct bch_fs *);
diff --git a/fs/bcachefs/sysfs.c b/fs/bcachefs/sysfs.c
index 34c5f9029bd6..e42bc1dae336 100644
--- a/fs/bcachefs/sysfs.c
+++ b/fs/bcachefs/sysfs.c
@@ -168,6 +168,7 @@ rw_attribute(writeback_pages_max);
rw_attribute(discard);
rw_attribute(cache_replacement_policy);
+rw_attribute(group);
rw_attribute(copy_gc_enabled);
sysfs_pd_controller_attribute(copy_gc);
@@ -525,9 +526,7 @@ SHOW(bch2_fs_opts_dir)
int id = opt - bch2_opt_table;
u64 v = bch2_opt_get_by_id(&c->opts, id);
- out += opt->type == BCH_OPT_STR
- ? bch2_scnprint_string_list(out, end - out, opt->choices, v)
- : scnprintf(out, end - out, "%lli", v);
+ out += bch2_opt_to_text(c, out, end - out, opt, v, OPT_SHOW_FULL_LIST);
out += scnprintf(out, end - out, "\n");
return out - buf;
@@ -540,7 +539,7 @@ STORE(bch2_fs_opts_dir)
int ret, id = opt - bch2_opt_table;
u64 v;
- ret = bch2_opt_parse(opt, buf, &v);
+ ret = bch2_opt_parse(c, opt, buf, &v);
if (ret < 0)
return ret;
@@ -812,6 +811,26 @@ SHOW(bch2_dev)
sysfs_print(nbuckets, ca->mi.nbuckets);
sysfs_print(discard, ca->mi.discard);
+ if (attr == &sysfs_group) {
+ struct bch_sb_field_disk_groups *groups;
+ struct bch_disk_group *g;
+ unsigned len;
+
+ if (!ca->mi.group)
+ return scnprintf(out, end - out, "none\n");
+
+ mutex_lock(&c->sb_lock);
+ groups = bch2_sb_get_disk_groups(c->disk_sb);
+
+ g = &groups->entries[ca->mi.group - 1];
+ len = strnlen(g->label, sizeof(g->label));
+ memcpy(buf, g->label, len);
+ mutex_unlock(&c->sb_lock);
+
+ buf[len++] = '\n';
+ return len;
+ }
+
if (attr == &sysfs_has_data) {
out += bch2_scnprint_flag_list(out, end - out,
bch2_data_types,
@@ -893,6 +912,12 @@ STORE(bch2_dev)
mutex_unlock(&c->sb_lock);
}
+ if (attr == &sysfs_group) {
+ int ret = bch2_dev_group_set(c, ca, buf);
+ if (ret)
+ return ret;
+ }
+
if (attr == &sysfs_wake_allocator)
bch2_wake_allocator(ca);
@@ -911,6 +936,7 @@ struct attribute *bch2_dev_files[] = {
&sysfs_discard,
&sysfs_cache_replacement_policy,
&sysfs_state_rw,
+ &sysfs_group,
&sysfs_has_data,
&sysfs_iostats,
diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c
index 70742fc8a5fc..81e942e5039c 100644
--- a/fs/bcachefs/xattr.c
+++ b/fs/bcachefs/xattr.c
@@ -367,6 +367,7 @@ static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
const char *name, void *buffer, size_t size)
{
struct bch_inode_info *inode = to_bch_ei(vinode);
+ struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct bch_opts opts =
bch2_inode_opts_to_opts(bch2_inode_opts_get(&inode->ei_inode));
const struct bch_option *opt;
@@ -384,12 +385,9 @@ static int bch2_xattr_bcachefs_get(const struct xattr_handler *handler,
v = bch2_opt_get_by_id(&opts, id);
- if (opt->type == BCH_OPT_STR)
- ret = snprintf(buffer, size, "%s", opt->choices[v]);
- else
- ret = snprintf(buffer, size, "%llu", v);
+ ret = bch2_opt_to_text(c, buffer, size, opt, v, 0);
- return ret <= size || !buffer ? ret : -ERANGE;
+ return ret < size || !buffer ? ret : -ERANGE;
}
struct inode_opt_set {
@@ -436,7 +434,7 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler,
memcpy(buf, value, size);
buf[size] = '\0';
- ret = bch2_opt_parse(opt, buf, &s.v);
+ ret = bch2_opt_parse(c, opt, buf, &s.v);
kfree(buf);
if (ret < 0)