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