]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs.c
Initramfs script improvements
[bcachefs-tools-debian] / libbcachefs.c
index 1481ef382583ab0e899e1f726234742ff4a57e8c..9baaff046cc36c324a05e941154991b7155fc70e 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
@@ -17,6 +18,7 @@
 #include "libbcachefs/checksum.h"
 #include "crypto.h"
 #include "libbcachefs.h"
+#include "libbcachefs/btree_cache.h"
 #include "libbcachefs/opts.h"
 #include "libbcachefs/super-io.h"
 
@@ -122,12 +124,41 @@ void bch2_pick_bucket_size(struct format_opts opts, struct dev_opts *dev)
 
 }
 
+static unsigned parse_target(struct dev_opts *devs, size_t nr_devs,
+                            struct bch_sb_field_disk_groups *gi,
+                            const char *s)
+{
+       struct bch_disk_group *g;
+       struct dev_opts *i;
+
+       if (!s)
+               return 0;
+
+       for (i = devs; i < devs + nr_devs; i++)
+               if (!strcmp(s, i->path))
+                       return dev_to_target(i - devs);
+
+       for (g = gi->entries;
+            g < gi->entries + disk_groups_nr(gi);
+            g++) {
+               unsigned len = strnlen(g->label, sizeof(g->label));
+
+               if (len == strlen(s) &&
+                   !memcmp(s, g->label, len))
+                       return group_to_target(g - gi->entries);
+       }
+
+       die("Invalid target %s", s);
+       return 0;
+}
+
 struct bch_sb *bch2_format(struct format_opts opts,
                           struct dev_opts *devs, size_t nr_devs)
 {
        struct bch_sb *sb;
        struct dev_opts *i;
        struct bch_sb_field_members *mi;
+       struct bch_sb_field_disk_groups *gi = NULL;
        unsigned u64s;
 
        /* calculate block size: */
@@ -162,6 +193,8 @@ struct bch_sb *bch2_format(struct format_opts opts,
        sb = calloc(1, sizeof(*sb) +
                    sizeof(struct bch_sb_field_members) +
                    sizeof(struct bch_member) * nr_devs +
+                   sizeof(struct bch_sb_field_disk_groups) +
+                   sizeof(struct bch_disk_group) * nr_devs +
                    sizeof(struct bch_sb_field_crypt));
 
        sb->version     = cpu_to_le64(BCH_SB_VERSION_MAX);
@@ -199,27 +232,15 @@ struct bch_sb *bch2_format(struct format_opts opts,
        sb->time_base_lo        = cpu_to_le64(now.tv_sec * NSEC_PER_SEC + now.tv_nsec);
        sb->time_precision      = cpu_to_le32(1);
 
-       if (opts.encrypted) {
-               struct bch_sb_field_crypt *crypt = vstruct_end(sb);
-
-               u64s = sizeof(struct bch_sb_field_crypt) / sizeof(u64);
-
-               le32_add_cpu(&sb->u64s, u64s);
-               crypt->field.u64s = cpu_to_le32(u64s);
-               crypt->field.type = BCH_SB_FIELD_crypt;
-
-               bch_sb_crypt_init(sb, crypt, opts.passphrase);
-               SET_BCH_SB_ENCRYPTION_TYPE(sb, 1);
-       }
-
        mi = vstruct_end(sb);
        u64s = (sizeof(struct bch_sb_field_members) +
                sizeof(struct bch_member) * nr_devs) / sizeof(u64);
 
        le32_add_cpu(&sb->u64s, u64s);
-       mi->field.u64s = cpu_to_le32(u64s);
+       le32_add_cpu(&mi->field.u64s, u64s);
        mi->field.type = BCH_SB_FIELD_members;
 
+       /* Member info: */
        for (i = devs; i < devs + nr_devs; i++) {
                struct bch_member *m = mi->members + (i - devs);
 
@@ -228,12 +249,67 @@ struct bch_sb *bch2_format(struct format_opts opts,
                m->first_bucket = 0;
                m->bucket_size  = cpu_to_le16(i->bucket_size);
 
-               SET_BCH_MEMBER_TIER(m,          i->tier);
                SET_BCH_MEMBER_REPLACEMENT(m,   CACHE_REPLACEMENT_LRU);
                SET_BCH_MEMBER_DISCARD(m,       i->discard);
                SET_BCH_MEMBER_DATA_ALLOWED(m,  i->data_allowed);
        }
 
+       /* Disk groups */
+       for (i = devs; i < devs + nr_devs; i++) {
+               struct bch_member *m = mi->members + (i - devs);
+               struct bch_disk_group *g;
+               size_t len;
+               int idx;
+
+               if (!i->group)
+                       continue;
+
+               len = min_t(size_t, strlen(i->group) + 1, BCH_SB_LABEL_SIZE);
+
+               if (!gi) {
+                       gi = vstruct_end(sb);
+                       u64s = sizeof(*gi) / sizeof(u64);
+                       le32_add_cpu(&sb->u64s, u64s);
+                       le32_add_cpu(&gi->field.u64s, u64s);
+                       gi->field.type = BCH_SB_FIELD_disk_groups;
+               }
+
+               idx = __bch2_disk_group_find(gi, i->group);
+               if (idx >= 0) {
+                       g = gi->entries + idx;
+               } else {
+                       u64s = sizeof(*g) / sizeof(u64);
+                       g = vstruct_end(&gi->field);
+                       le32_add_cpu(&sb->u64s, u64s);
+                       le32_add_cpu(&gi->field.u64s, u64s);
+                       memcpy(g->label, i->group, len);
+                       SET_BCH_GROUP_DATA_ALLOWED(g, ~0);
+               }
+
+               SET_BCH_MEMBER_GROUP(m, (g - gi->entries) + 1);
+       }
+
+       SET_BCH_SB_FOREGROUND_TARGET(sb,
+               parse_target(devs, nr_devs, gi, opts.foreground_target));
+       SET_BCH_SB_BACKGROUND_TARGET(sb,
+               parse_target(devs, nr_devs, gi, opts.background_target));
+       SET_BCH_SB_PROMOTE_TARGET(sb,
+               parse_target(devs, nr_devs, gi, opts.promote_target));
+
+       /* Crypt: */
+       if (opts.encrypted) {
+               struct bch_sb_field_crypt *crypt = vstruct_end(sb);
+
+               u64s = sizeof(struct bch_sb_field_crypt) / sizeof(u64);
+
+               le32_add_cpu(&sb->u64s, u64s);
+               crypt->field.u64s = cpu_to_le32(u64s);
+               crypt->field.type = BCH_SB_FIELD_crypt;
+
+               bch_sb_crypt_init(sb, crypt, opts.passphrase);
+               SET_BCH_SB_ENCRYPTION_TYPE(sb, 1);
+       }
+
        for (i = devs; i < devs + nr_devs; i++) {
                sb->dev_idx = i - devs;
 
@@ -353,6 +429,7 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
                                  enum units units)
 {
        struct bch_sb_field_members *mi = field_to_type(f, members);
+       struct bch_sb_field_disk_groups *gi = bch2_sb_get_disk_groups(sb);
        unsigned i;
 
        for (i = 0; i < sb->nr_devices; i++) {
@@ -361,11 +438,21 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
                char member_uuid_str[40];
                char data_allowed_str[100];
                char data_has_str[100];
+               char group[64];
 
                if (!bch2_member_exists(m))
                        continue;
 
                uuid_unparse(m->uuid.b, member_uuid_str);
+
+               if (BCH_MEMBER_GROUP(m)) {
+                       if (BCH_MEMBER_GROUP(m) < disk_groups_nr(gi))
+                               memcpy(group, gi->entries[BCH_MEMBER_GROUP(m)].label,
+                                      BCH_SB_LABEL_SIZE);
+                       else
+                               strcpy(group, "(bad disk groups section");
+               }
+
                bch2_scnprint_flag_list(data_allowed_str,
                                        sizeof(data_allowed_str),
                                        bch2_data_types,
@@ -388,7 +475,7 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
                       "    Buckets:                    %llu\n"
                       "    Last mount:                 %s\n"
                       "    State:                      %s\n"
-                      "    Tier:                       %llu\n"
+                      "    Group:                      %s\n"
                       "    Data allowed:               %s\n"
 
                       "    Has data:                   %s\n"
@@ -407,7 +494,7 @@ static void bch2_sb_print_members(struct bch_sb *sb, struct bch_sb_field *f,
                       ? bch2_dev_state[BCH_MEMBER_STATE(m)]
                       : "unknown",
 
-                      BCH_MEMBER_TIER(m),
+                      group,
                       data_allowed_str,
                       data_has_str,
 
@@ -454,6 +541,16 @@ static void bch2_sb_print_replicas(struct bch_sb *sb, struct bch_sb_field *f,
        }
 }
 
+static void bch2_sb_print_quota(struct bch_sb *sb, struct bch_sb_field *f,
+                               enum units units)
+{
+}
+
+static void bch2_sb_print_disk_groups(struct bch_sb *sb, struct bch_sb_field *f,
+                                     enum units units)
+{
+}
+
 typedef void (*sb_field_print_fn)(struct bch_sb *, struct bch_sb_field *, enum units);
 
 struct bch_sb_field_ops {
@@ -528,6 +625,10 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
               "Data checksum type:             %s (%llu)\n"
               "Compression type:               %s (%llu)\n"
 
+              "Foreground write target:        %llu\n"
+              "Background write target:        %llu\n"
+              "Promote target:                 %llu\n"
+
               "String hash type:               %s (%llu)\n"
               "32 bit inodes:                  %llu\n"
               "GC reserve percentage:          %llu%%\n"
@@ -567,6 +668,10 @@ void bch2_sb_print(struct bch_sb *sb, bool print_layout,
               : "unknown",
               BCH_SB_COMPRESSION_TYPE(sb),
 
+              BCH_SB_FOREGROUND_TARGET(sb),
+              BCH_SB_BACKGROUND_TARGET(sb),
+              BCH_SB_PROMOTE_TARGET(sb),
+
               BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
               ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)]
               : "unknown",
@@ -630,7 +735,7 @@ struct bchfs_handle bcache_fs_open(const char *path)
 
        if (!uuid_parse(path, ret.uuid.b)) {
                /* It's a UUID, look it up in sysfs: */
-               char *sysfs = mprintf("%s%s", SYSFS_BASE, path);
+               char *sysfs = mprintf(SYSFS_BASE "%s", path);
                ret.sysfs_fd = xopen(sysfs, O_RDONLY);
 
                char *minor = read_file_str(ret.sysfs_fd, "minor");
@@ -652,10 +757,94 @@ struct bchfs_handle bcache_fs_open(const char *path)
                char uuid_str[40];
                uuid_unparse(uuid.uuid.b, uuid_str);
 
-               char *sysfs = mprintf("%s%s", SYSFS_BASE, uuid_str);
+               char *sysfs = mprintf(SYSFS_BASE "%s", uuid_str);
                ret.sysfs_fd = xopen(sysfs, O_RDONLY);
                free(sysfs);
        }
 
        return ret;
 }
+
+/*
+ * Given a path to a block device, open the filesystem it belongs to; also
+ * return the device's idx:
+ */
+struct bchfs_handle bchu_fs_open_by_dev(const char *path, unsigned *idx)
+{
+       char buf[1024], *uuid_str;
+
+       struct stat stat = xstat(path);
+
+       if (!S_ISBLK(stat.st_mode))
+               die("%s is not a block device", path);
+
+       char *sysfs = mprintf("/sys/dev/block/%u:%u/bcachefs",
+                             major(stat.st_dev),
+                             minor(stat.st_dev));
+       ssize_t len = readlink(sysfs, buf, sizeof(buf));
+       free(sysfs);
+
+       if (len > 0) {
+               char *p = strrchr(buf, '/');
+               if (!p || sscanf(p + 1, "dev-%u", idx) != 1)
+                       die("error parsing sysfs");
+
+               *p = '\0';
+               p = strrchr(buf, '/');
+               uuid_str = p + 1;
+       } else {
+               struct bch_opts opts = bch2_opts_empty();
+
+               opt_set(opts, noexcl,   true);
+               opt_set(opts, nochanges, true);
+
+               struct bch_sb_handle sb;
+               int ret = bch2_read_super(path, &opts, &sb);
+               if (ret)
+                       die("Error opening %s: %s", path, strerror(-ret));
+
+               *idx = sb.sb->dev_idx;
+               uuid_str = buf;
+               uuid_unparse(sb.sb->user_uuid.b, uuid_str);
+
+               bch2_free_super(&sb);
+       }
+
+       return bcache_fs_open(uuid_str);
+}
+
+int bchu_data(struct bchfs_handle fs, struct bch_ioctl_data cmd)
+{
+       int progress_fd = xioctl(fs.ioctl_fd, BCH_IOCTL_DATA, &cmd);
+
+       while (1) {
+               struct bch_ioctl_data_progress p;
+
+               if (read(progress_fd, &p, sizeof(p)) != sizeof(p))
+                       die("error reading from progress fd");
+
+               if (p.data_type == U8_MAX)
+                       break;
+
+               printf("\33[2K\r");
+
+               printf("%llu%% complete: current position %s",
+                      p.sectors_done * 100 / p.sectors_total,
+                      bch2_data_types[p.data_type]);
+
+               switch (p.data_type) {
+               case BCH_DATA_BTREE:
+               case BCH_DATA_USER:
+                       printf(" %s:%llu:%llu",
+                              bch2_btree_ids[p.btree_id],
+                              p.pos.inode,
+                              p.pos.offset);
+               }
+
+               sleep(1);
+       }
+       printf("\nDone\n");
+
+       close(progress_fd);
+       return 0;
+}