1 // SPDX-License-Identifier: GPL-2.0
3 #include "disk_groups.h"
6 #include <linux/sort.h>
8 static int group_cmp(const void *_l, const void *_r)
10 const struct bch_disk_group *l = _l;
11 const struct bch_disk_group *r = _r;
13 return ((BCH_GROUP_DELETED(l) > BCH_GROUP_DELETED(r)) -
14 (BCH_GROUP_DELETED(l) < BCH_GROUP_DELETED(r))) ?:
15 ((BCH_GROUP_PARENT(l) > BCH_GROUP_PARENT(r)) -
16 (BCH_GROUP_PARENT(l) < BCH_GROUP_PARENT(r))) ?:
17 strncmp(l->label, r->label, sizeof(l->label));
20 static const char *bch2_sb_disk_groups_validate(struct bch_sb *sb,
21 struct bch_sb_field *f)
23 struct bch_sb_field_disk_groups *groups =
24 field_to_type(f, disk_groups);
25 struct bch_disk_group *g, *sorted = NULL;
26 struct bch_sb_field_members *mi;
28 unsigned i, nr_groups, len;
29 const char *err = NULL;
31 mi = bch2_sb_get_members(sb);
32 groups = bch2_sb_get_disk_groups(sb);
33 nr_groups = disk_groups_nr(groups);
36 m < mi->members + sb->nr_devices;
40 if (!BCH_MEMBER_GROUP(m))
43 g = BCH_MEMBER_GROUP(m) - 1;
46 BCH_GROUP_DELETED(&groups->entries[g]))
47 return "disk has invalid group";
53 for (g = groups->entries;
54 g < groups->entries + nr_groups;
56 if (BCH_GROUP_DELETED(g))
59 len = strnlen(g->label, sizeof(g->label));
61 err = "group with empty label";
66 sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
68 return "cannot allocate memory";
70 memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
71 sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);
73 for (i = 0; i + 1 < nr_groups; i++)
74 if (!BCH_GROUP_DELETED(sorted + i) &&
75 !group_cmp(sorted + i, sorted + i + 1)) {
76 err = "duplicate groups";
86 static void bch2_sb_disk_groups_to_text(struct printbuf *out,
88 struct bch_sb_field *f)
90 struct bch_sb_field_disk_groups *groups =
91 field_to_type(f, disk_groups);
92 struct bch_disk_group *g;
93 unsigned nr_groups = disk_groups_nr(groups);
95 for (g = groups->entries;
96 g < groups->entries + nr_groups;
98 if (g != groups->entries)
101 if (BCH_GROUP_DELETED(g))
102 pr_buf(out, "[deleted]");
104 pr_buf(out, "[parent %llu name %s]",
105 BCH_GROUP_PARENT(g), g->label);
109 const struct bch_sb_field_ops bch_sb_field_ops_disk_groups = {
110 .validate = bch2_sb_disk_groups_validate,
111 .to_text = bch2_sb_disk_groups_to_text
114 int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
116 struct bch_sb_field_members *mi;
117 struct bch_sb_field_disk_groups *groups;
118 struct bch_disk_groups_cpu *cpu_g, *old_g;
119 unsigned i, g, nr_groups;
121 lockdep_assert_held(&c->sb_lock);
123 mi = bch2_sb_get_members(c->disk_sb.sb);
124 groups = bch2_sb_get_disk_groups(c->disk_sb.sb);
125 nr_groups = disk_groups_nr(groups);
130 cpu_g = kzalloc(sizeof(*cpu_g) +
131 sizeof(cpu_g->entries[0]) * nr_groups, GFP_KERNEL);
135 cpu_g->nr = nr_groups;
137 for (i = 0; i < nr_groups; i++) {
138 struct bch_disk_group *src = &groups->entries[i];
139 struct bch_disk_group_cpu *dst = &cpu_g->entries[i];
141 dst->deleted = BCH_GROUP_DELETED(src);
142 dst->parent = BCH_GROUP_PARENT(src);
145 for (i = 0; i < c->disk_sb.sb->nr_devices; i++) {
146 struct bch_member *m = mi->members + i;
147 struct bch_disk_group_cpu *dst =
148 &cpu_g->entries[BCH_MEMBER_GROUP(m)];
150 if (!bch2_member_exists(m))
153 g = BCH_MEMBER_GROUP(m);
155 dst = &cpu_g->entries[g - 1];
156 __set_bit(i, dst->devs.d);
161 old_g = rcu_dereference_protected(c->disk_groups,
162 lockdep_is_held(&c->sb_lock));
163 rcu_assign_pointer(c->disk_groups, cpu_g);
165 kfree_rcu(old_g, rcu);
170 const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
172 struct target t = target_decode(target);
178 struct bch_dev *ca = t.dev < c->sb.nr_devices
179 ? rcu_dereference(c->devs[t.dev])
181 return ca ? &ca->self : NULL;
184 struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
186 return t.group < g->nr && !g->entries[t.group].deleted
187 ? &g->entries[t.group].devs
195 bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
197 struct target t = target_decode(target);
205 struct bch_disk_groups_cpu *g;
206 const struct bch_devs_mask *m;
210 g = rcu_dereference(c->disk_groups);
211 m = t.group < g->nr && !g->entries[t.group].deleted
212 ? &g->entries[t.group].devs
215 ret = m ? test_bit(dev, m->d) : false;
225 static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
227 const char *name, unsigned namelen)
229 unsigned i, nr_groups = disk_groups_nr(groups);
231 if (!namelen || namelen > BCH_SB_LABEL_SIZE)
234 for (i = 0; i < nr_groups; i++) {
235 struct bch_disk_group *g = groups->entries + i;
237 if (BCH_GROUP_DELETED(g))
240 if (!BCH_GROUP_DELETED(g) &&
241 BCH_GROUP_PARENT(g) == parent &&
242 strnlen(g->label, sizeof(g->label)) == namelen &&
243 !memcmp(name, g->label, namelen))
250 static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
251 const char *name, unsigned namelen)
253 struct bch_sb_field_disk_groups *groups =
254 bch2_sb_get_disk_groups(sb->sb);
255 unsigned i, nr_groups = disk_groups_nr(groups);
256 struct bch_disk_group *g;
258 if (!namelen || namelen > BCH_SB_LABEL_SIZE)
262 i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
266 if (i == nr_groups) {
268 (sizeof(struct bch_sb_field_disk_groups) +
269 sizeof(struct bch_disk_group) * (nr_groups + 1)) /
272 groups = bch2_sb_resize_disk_groups(sb, u64s);
276 nr_groups = disk_groups_nr(groups);
279 BUG_ON(i >= nr_groups);
281 g = &groups->entries[i];
283 memcpy(g->label, name, namelen);
284 if (namelen < sizeof(g->label))
285 g->label[namelen] = '\0';
286 SET_BCH_GROUP_DELETED(g, 0);
287 SET_BCH_GROUP_PARENT(g, parent);
288 SET_BCH_GROUP_DATA_ALLOWED(g, ~0);
293 int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)
295 struct bch_sb_field_disk_groups *groups =
296 bch2_sb_get_disk_groups(sb->sb);
300 const char *next = strchrnul(name, '.');
301 unsigned len = next - name;
306 v = __bch2_disk_group_find(groups, v + 1, name, len);
308 } while (*name && v >= 0);
313 int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
315 struct bch_sb_field_disk_groups *groups;
320 const char *next = strchrnul(name, '.');
321 unsigned len = next - name;
326 groups = bch2_sb_get_disk_groups(sb->sb);
328 v = __bch2_disk_group_find(groups, parent, name, len);
330 v = __bch2_disk_group_add(sb, parent, name, len);
336 } while (*name && v >= 0);
341 void bch2_disk_path_to_text(struct printbuf *out,
342 struct bch_sb_handle *sb,
345 struct bch_sb_field_disk_groups *groups =
346 bch2_sb_get_disk_groups(sb->sb);
347 struct bch_disk_group *g;
352 if (nr == ARRAY_SIZE(path))
355 if (v >= disk_groups_nr(groups))
358 g = groups->entries + v;
360 if (BCH_GROUP_DELETED(g))
365 if (!BCH_GROUP_PARENT(g))
368 v = BCH_GROUP_PARENT(g) - 1;
373 g = groups->entries + v;
375 bch_scnmemcpy(out, g->label,
376 strnlen(g->label, sizeof(g->label)));
383 pr_buf(out, "invalid group %u", v);
386 int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
388 struct bch_member *mi;
391 mutex_lock(&c->sb_lock);
393 if (!strlen(name) || !strcmp(name, "none"))
396 v = bch2_disk_path_find_or_create(&c->disk_sb, name);
398 mutex_unlock(&c->sb_lock);
403 mi = &bch2_sb_get_members(c->disk_sb.sb)->members[ca->dev_idx];
404 SET_BCH_MEMBER_GROUP(mi, v + 1);
407 mutex_unlock(&c->sb_lock);
412 int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
417 if (!strlen(buf) || !strcmp(buf, "none")) {
422 /* Is it a device? */
423 ca = bch2_dev_lookup(c, buf);
425 *v = dev_to_target(ca->dev_idx);
426 percpu_ref_put(&ca->ref);
430 mutex_lock(&c->sb_lock);
431 g = bch2_disk_path_find(&c->disk_sb, buf);
432 mutex_unlock(&c->sb_lock);
435 *v = group_to_target(g);
442 void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v)
444 struct target t = target_decode(v);
454 ca = t.dev < c->sb.nr_devices
455 ? rcu_dereference(c->devs[t.dev])
458 if (ca && percpu_ref_tryget(&ca->io_ref)) {
459 char b[BDEVNAME_SIZE];
461 pr_buf(out, "/dev/%s",
462 bdevname(ca->disk_sb.bdev, b));
463 percpu_ref_put(&ca->io_ref);
465 pr_buf(out, "offline device %u", t.dev);
467 pr_buf(out, "invalid device %u", t.dev);
474 mutex_lock(&c->sb_lock);
475 bch2_disk_path_to_text(out, &c->disk_sb, t.group);
476 mutex_unlock(&c->sb_lock);