bcachefs: On device add, prefer unused slots
authorKent Overstreet <kent.overstreet@linux.dev>
Tue, 30 Apr 2024 08:24:58 +0000 (04:24 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Wed, 8 May 2024 21:29:22 +0000 (17:29 -0400)
We can't strictly guarantee that no pointers refer to nonexistent
devices - we attempt to, but we need to be safe when the filesystem is
corrupt.

Therefore, change device_add to try to pick a slot that's never been
used, or the slot that's been unused the longest.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/super.c

index 1cbb2b3f474051f76310b5dfd2558e45e57fef62..6c6087039781e417141cc6def15f499bb5f15760 100644 (file)
@@ -1764,9 +1764,28 @@ int bch2_dev_add(struct bch_fs *c, const char *path)
        if (dynamic_fault("bcachefs:add:no_slot"))
                goto no_slot;
 
-       for (dev_idx = 0; dev_idx < BCH_SB_MEMBERS_MAX; dev_idx++)
-               if (!bch2_member_exists(c->disk_sb.sb, dev_idx))
-                       goto have_slot;
+       if (c->sb.nr_devices < BCH_SB_MEMBERS_MAX) {
+               dev_idx = c->sb.nr_devices;
+               goto have_slot;
+       }
+
+       int best = -1;
+       u64 best_last_mount = 0;
+       for (dev_idx = 0; dev_idx < BCH_SB_MEMBERS_MAX; dev_idx++) {
+               struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, dev_idx);
+               if (bch2_member_alive(&m))
+                       continue;
+
+               u64 last_mount = le64_to_cpu(m.last_mount);
+               if (best < 0 || last_mount < best_last_mount) {
+                       best = dev_idx;
+                       best_last_mount = last_mount;
+               }
+       }
+       if (best >= 0) {
+               dev_idx = best;
+               goto have_slot;
+       }
 no_slot:
        ret = -BCH_ERR_ENOSPC_sb_members;
        bch_err_msg(c, ret, "setting up new superblock");