]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/disk_groups.c
Update bcachefs sources to b91a514413 bcachefs: Don't try to delete stripes when RO
[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 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 = 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
391         mutex_lock(&c->sb_lock);
392
393         if (!strlen(name) || !strcmp(name, "none"))
394                 goto write_sb;
395
396         v = bch2_disk_path_find_or_create(&c->disk_sb, name);
397         if (v < 0) {
398                 mutex_unlock(&c->sb_lock);
399                 return v;
400         }
401
402 write_sb:
403         mi = &bch2_sb_get_members(c->disk_sb.sb)->members[ca->dev_idx];
404         SET_BCH_MEMBER_GROUP(mi, v + 1);
405
406         bch2_write_super(c);
407         mutex_unlock(&c->sb_lock);
408
409         return 0;
410 }
411
412 int bch2_opt_target_parse(struct bch_fs *c, const char *buf, u64 *v)
413 {
414         struct bch_dev *ca;
415         int g;
416
417         if (!strlen(buf) || !strcmp(buf, "none")) {
418                 *v = 0;
419                 return 0;
420         }
421
422         /* Is it a device? */
423         ca = bch2_dev_lookup(c, buf);
424         if (!IS_ERR(ca)) {
425                 *v = dev_to_target(ca->dev_idx);
426                 percpu_ref_put(&ca->ref);
427                 return 0;
428         }
429
430         mutex_lock(&c->sb_lock);
431         g = bch2_disk_path_find(&c->disk_sb, buf);
432         mutex_unlock(&c->sb_lock);
433
434         if (g >= 0) {
435                 *v = group_to_target(g);
436                 return 0;
437         }
438
439         return -EINVAL;
440 }
441
442 void bch2_opt_target_to_text(struct printbuf *out, struct bch_fs *c, u64 v)
443 {
444         struct target t = target_decode(v);
445
446         switch (t.type) {
447         case TARGET_NULL:
448                 pr_buf(out, "none");
449                 break;
450         case TARGET_DEV: {
451                 struct bch_dev *ca;
452
453                 rcu_read_lock();
454                 ca = t.dev < c->sb.nr_devices
455                         ? rcu_dereference(c->devs[t.dev])
456                         : NULL;
457
458                 if (ca && percpu_ref_tryget(&ca->io_ref)) {
459                         char b[BDEVNAME_SIZE];
460
461                         pr_buf(out, "/dev/%s",
462                              bdevname(ca->disk_sb.bdev, b));
463                         percpu_ref_put(&ca->io_ref);
464                 } else if (ca) {
465                         pr_buf(out, "offline device %u", t.dev);
466                 } else {
467                         pr_buf(out, "invalid device %u", t.dev);
468                 }
469
470                 rcu_read_unlock();
471                 break;
472         }
473         case TARGET_GROUP:
474                 mutex_lock(&c->sb_lock);
475                 bch2_disk_path_to_text(out, &c->disk_sb, t.group);
476                 mutex_unlock(&c->sb_lock);
477                 break;
478         default:
479                 BUG();
480         }
481 }