]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/disk_groups.c
Update bcachefs sources to 10ab39f2fa bcachefs: Improvements to the journal read...
[bcachefs-tools-debian] / libbcachefs / disk_groups.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include "bcachefs.h"
3 #include "disk_groups.h"
4 #include "super-io.h"
5
6 #include <linux/sort.h>
7
8 static int group_cmp(const void *_l, const void *_r)
9 {
10         const struct bch_disk_group *l = _l;
11         const struct bch_disk_group *r = _r;
12
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));
18 }
19
20 static const char *bch2_sb_disk_groups_validate(struct bch_sb *sb,
21                                                 struct bch_sb_field *f)
22 {
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;
27         struct bch_member *m;
28         unsigned i, nr_groups, len;
29         const char *err = NULL;
30
31         mi              = bch2_sb_get_members(sb);
32         groups          = bch2_sb_get_disk_groups(sb);
33         nr_groups       = disk_groups_nr(groups);
34
35         for (m = mi->members;
36              m < mi->members + sb->nr_devices;
37              m++) {
38                 unsigned g;
39
40                 if (!BCH_MEMBER_GROUP(m))
41                         continue;
42
43                 g = BCH_MEMBER_GROUP(m) - 1;
44
45                 if (g >= nr_groups ||
46                     BCH_GROUP_DELETED(&groups->entries[g]))
47                         return "disk has invalid group";
48         }
49
50         if (!nr_groups)
51                 return NULL;
52
53         for (g = groups->entries;
54              g < groups->entries + nr_groups;
55              g++) {
56                 if (BCH_GROUP_DELETED(g))
57                         continue;
58
59                 len = strnlen(g->label, sizeof(g->label));
60                 if (!len) {
61                         err = "group with empty label";
62                         goto err;
63                 }
64         }
65
66         sorted = kmalloc_array(nr_groups, sizeof(*sorted), GFP_KERNEL);
67         if (!sorted)
68                 return "cannot allocate memory";
69
70         memcpy(sorted, groups->entries, nr_groups * sizeof(*sorted));
71         sort(sorted, nr_groups, sizeof(*sorted), group_cmp, NULL);
72
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";
77                         goto err;
78                 }
79
80         err = NULL;
81 err:
82         kfree(sorted);
83         return err;
84 }
85
86 static void bch2_sb_disk_groups_to_text(struct printbuf *out,
87                                         struct bch_sb *sb,
88                                         struct bch_sb_field *f)
89 {
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);
94
95         for (g = groups->entries;
96              g < groups->entries + nr_groups;
97              g++) {
98                 if (g != groups->entries)
99                         pr_buf(out, " ");
100
101                 if (BCH_GROUP_DELETED(g))
102                         pr_buf(out, "[deleted]");
103                 else
104                         pr_buf(out, "[parent %llu name %s]",
105                                BCH_GROUP_PARENT(g), g->label);
106         }
107 }
108
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
112 };
113
114 int bch2_sb_disk_groups_to_cpu(struct bch_fs *c)
115 {
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;
120
121         lockdep_assert_held(&c->sb_lock);
122
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);
126
127         if (!groups)
128                 return 0;
129
130         cpu_g = kzalloc(sizeof(*cpu_g) +
131                         sizeof(cpu_g->entries[0]) * nr_groups, GFP_KERNEL);
132         if (!cpu_g)
133                 return -ENOMEM;
134
135         cpu_g->nr = nr_groups;
136
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];
140
141                 dst->deleted    = BCH_GROUP_DELETED(src);
142                 dst->parent     = BCH_GROUP_PARENT(src);
143         }
144
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)];
149
150                 if (!bch2_member_exists(m))
151                         continue;
152
153                 g = BCH_MEMBER_GROUP(m);
154                 while (g) {
155                         dst = &cpu_g->entries[g - 1];
156                         __set_bit(i, dst->devs.d);
157                         g = dst->parent;
158                 }
159         }
160
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);
164         if (old_g)
165                 kfree_rcu(old_g, rcu);
166
167         return 0;
168 }
169
170 const struct bch_devs_mask *bch2_target_to_mask(struct bch_fs *c, unsigned target)
171 {
172         struct target t = target_decode(target);
173
174         switch (t.type) {
175         case TARGET_NULL:
176                 return NULL;
177         case TARGET_DEV: {
178                 struct bch_dev *ca = t.dev < c->sb.nr_devices
179                         ? rcu_dereference(c->devs[t.dev])
180                         : NULL;
181                 return ca ? &ca->self : NULL;
182         }
183         case TARGET_GROUP: {
184                 struct bch_disk_groups_cpu *g = rcu_dereference(c->disk_groups);
185
186                 return g && t.group < g->nr && !g->entries[t.group].deleted
187                         ? &g->entries[t.group].devs
188                         : NULL;
189         }
190         default:
191                 BUG();
192         }
193 }
194
195 bool bch2_dev_in_target(struct bch_fs *c, unsigned dev, unsigned target)
196 {
197         struct target t = target_decode(target);
198
199         switch (t.type) {
200         case TARGET_NULL:
201                 return false;
202         case TARGET_DEV:
203                 return dev == t.dev;
204         case TARGET_GROUP: {
205                 struct bch_disk_groups_cpu *g;
206                 const struct bch_devs_mask *m;
207                 bool ret;
208
209                 rcu_read_lock();
210                 g = rcu_dereference(c->disk_groups);
211                 m = g && t.group < g->nr && !g->entries[t.group].deleted
212                         ? &g->entries[t.group].devs
213                         : NULL;
214
215                 ret = m ? test_bit(dev, m->d) : false;
216                 rcu_read_unlock();
217
218                 return ret;
219         }
220         default:
221                 BUG();
222         }
223 }
224
225 static int __bch2_disk_group_find(struct bch_sb_field_disk_groups *groups,
226                                   unsigned parent,
227                                   const char *name, unsigned namelen)
228 {
229         unsigned i, nr_groups = disk_groups_nr(groups);
230
231         if (!namelen || namelen > BCH_SB_LABEL_SIZE)
232                 return -EINVAL;
233
234         for (i = 0; i < nr_groups; i++) {
235                 struct bch_disk_group *g = groups->entries + i;
236
237                 if (BCH_GROUP_DELETED(g))
238                         continue;
239
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))
244                         return i;
245         }
246
247         return -1;
248 }
249
250 static int __bch2_disk_group_add(struct bch_sb_handle *sb, unsigned parent,
251                                  const char *name, unsigned namelen)
252 {
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;
257
258         if (!namelen || namelen > BCH_SB_LABEL_SIZE)
259                 return -EINVAL;
260
261         for (i = 0;
262              i < nr_groups && !BCH_GROUP_DELETED(&groups->entries[i]);
263              i++)
264                 ;
265
266         if (i == nr_groups) {
267                 unsigned u64s =
268                         (sizeof(struct bch_sb_field_disk_groups) +
269                          sizeof(struct bch_disk_group) * (nr_groups + 1)) /
270                         sizeof(u64);
271
272                 groups = bch2_sb_resize_disk_groups(sb, u64s);
273                 if (!groups)
274                         return -ENOSPC;
275
276                 nr_groups = disk_groups_nr(groups);
277         }
278
279         BUG_ON(i >= nr_groups);
280
281         g = &groups->entries[i];
282
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);
289
290         return i;
291 }
292
293 int bch2_disk_path_find(struct bch_sb_handle *sb, const char *name)
294 {
295         struct bch_sb_field_disk_groups *groups =
296                 bch2_sb_get_disk_groups(sb->sb);
297         int v = -1;
298
299         do {
300                 const char *next = strchrnul(name, '.');
301                 unsigned len = next - name;
302
303                 if (*next == '.')
304                         next++;
305
306                 v = __bch2_disk_group_find(groups, v + 1, name, len);
307                 name = next;
308         } while (*name && v >= 0);
309
310         return v;
311 }
312
313 int bch2_disk_path_find_or_create(struct bch_sb_handle *sb, const char *name)
314 {
315         struct bch_sb_field_disk_groups *groups;
316         unsigned parent = 0;
317         int v = -1;
318
319         do {
320                 const char *next = strchrnul(name, '.');
321                 unsigned len = next - name;
322
323                 if (*next == '.')
324                         next++;
325
326                 groups = bch2_sb_get_disk_groups(sb->sb);
327
328                 v = __bch2_disk_group_find(groups, parent, name, len);
329                 if (v < 0)
330                         v = __bch2_disk_group_add(sb, parent, name, len);
331                 if (v < 0)
332                         return v;
333
334                 parent = v + 1;
335                 name = next;
336         } while (*name && v >= 0);
337
338         return v;
339 }
340
341 void bch2_disk_path_to_text(struct printbuf *out,
342                             struct bch_sb_handle *sb,
343                             unsigned v)
344 {
345         struct bch_sb_field_disk_groups *groups =
346                 bch2_sb_get_disk_groups(sb->sb);
347         struct bch_disk_group *g;
348         unsigned nr = 0;
349         u16 path[32];
350
351         while (1) {
352                 if (nr == ARRAY_SIZE(path))
353                         goto inval;
354
355                 if (v >= disk_groups_nr(groups))
356                         goto inval;
357
358                 g = groups->entries + v;
359
360                 if (BCH_GROUP_DELETED(g))
361                         goto inval;
362
363                 path[nr++] = v;
364
365                 if (!BCH_GROUP_PARENT(g))
366                         break;
367
368                 v = BCH_GROUP_PARENT(g) - 1;
369         }
370
371         while (nr) {
372                 v = path[--nr];
373                 g = groups->entries + v;
374
375                 bch_scnmemcpy(out, g->label,
376                               strnlen(g->label, sizeof(g->label)));
377
378                 if (nr)
379                         pr_buf(out, ".");
380         }
381         return;
382 inval:
383         pr_buf(out, "invalid group %u", v);
384 }
385
386 int bch2_dev_group_set(struct bch_fs *c, struct bch_dev *ca, const char *name)
387 {
388         struct bch_member *mi;
389         int v = -1;
390         int ret = 0;
391
392         mutex_lock(&c->sb_lock);
393
394         if (!strlen(name) || !strcmp(name, "none"))
395                 goto write_sb;
396
397         v = bch2_disk_path_find_or_create(&c->disk_sb, name);
398         if (v < 0) {
399                 mutex_unlock(&c->sb_lock);
400                 return v;
401         }
402
403         ret = bch2_sb_disk_groups_to_cpu(c);
404         if (ret)
405                 goto unlock;
406 write_sb:
407         mi = &bch2_sb_get_members(c->disk_sb.sb)->members[ca->dev_idx];
408         SET_BCH_MEMBER_GROUP(mi, v + 1);
409
410         bch2_write_super(c);
411 unlock:
412         mutex_unlock(&c->sb_lock);
413
414         return ret;
415 }
416
417 int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
418 {
419         struct bch_dev *ca;
420         int g;
421
422         if (!strlen(buf) || !strcmp(buf, "none")) {
423                 *v = 0;
424                 return 0;
425         }
426
427         /* Is it a device? */
428         ca = bch2_dev_lookup(c, buf);
429         if (!IS_ERR(ca)) {
430                 *v = dev_to_target(ca->dev_idx);
431                 percpu_ref_put(&ca->ref);
432                 return 0;
433         }
434
435         mutex_lock(&c->sb_lock);
436         g = bch2_disk_path_find(&c->disk_sb, buf);
437         mutex_unlock(&c->sb_lock);
438
439         if (g >= 0) {
440                 *v = group_to_target(g);
441                 return 0;
442         }
443
444         return -EINVAL;
445 }
446
447 void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v)
448 {
449         struct target t = target_decode(v);
450
451         switch (t.type) {
452         case TARGET_NULL:
453                 pr_buf(out, "none");
454                 break;
455         case TARGET_DEV: {
456                 struct bch_dev *ca;
457
458                 rcu_read_lock();
459                 ca = t.dev < c->sb.nr_devices
460                         ? rcu_dereference(c->devs[t.dev])
461                         : NULL;
462
463                 if (ca && percpu_ref_tryget(&ca->io_ref)) {
464                         char b[BDEVNAME_SIZE];
465
466                         pr_buf(out, "/dev/%s",
467                              bdevname(ca->disk_sb.bdev, b));
468                         percpu_ref_put(&ca->io_ref);
469                 } else if (ca) {
470                         pr_buf(out, "offline device %u", t.dev);
471                 } else {
472                         pr_buf(out, "invalid device %u", t.dev);
473                 }
474
475                 rcu_read_unlock();
476                 break;
477         }
478         case TARGET_GROUP:
479                 mutex_lock(&c->sb_lock);
480                 bch2_disk_path_to_text(out, &c->disk_sb, t.group);
481                 mutex_unlock(&c->sb_lock);
482                 break;
483         default:
484                 BUG();
485         }
486 }