Whamcloud - gitweb
LU-16750 mke2fs: add "-E iops" to set IOPS storage group 35/51735/5
authorBobi Jam <bobijam@whamcloud.com>
Wed, 19 Jul 2023 10:07:05 +0000 (18:07 +0800)
committerAndreas Dilger <adilger@whamcloud.com>
Mon, 7 Aug 2023 14:09:42 +0000 (14:09 +0000)
With LVM it is possible to create an LV with SSD storage at the
beginning of the LV and HDD storage at the end of the LV, and use that
to separate ext4 metadata allocations (that need small random IOs)
from data allocations (that are better suited for large sequential
IOs) depending on the type of underlying storage.  Between 0.5-1.0% of
the filesystem capacity would need to be high-IOPS storage in order to
hold all of the internal metadata.

This would improve performance for inode and other metadata access,
such as ls, find, e2fsck, and in general improve file access latency,
modification, truncate, unlink, transaction commit, etc.

For mke2fs, using the sparse_super2 and packed_meta_blocks options
places all of the static metadata (group descriptors, block/inode
bitmaps, inode tables, journal) at the start of the device in the
(IOPS) flash region.

Add an option to mark which blocks are in the IOPS region of storage
at format time:

  -E iops=0-1024G,4096-8192G

so the ext4 mballoc code can then use the EXT4_BG_IOPS flag in the
group descriptors to decide which groups to allocate dynamic
filesystem metadata.

Change-Id: I13cc2820c71737848eab8a2d6e246748258a64df
Signed-off-by: Bobi Jam <bobijam@whamcloud.com>
Reviewed-on: https://review.whamcloud.com/c/tools/e2fsprogs/+/51735
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
debugfs/debugfs.c
lib/e2p/ls.c
lib/ext2fs/ext2_fs.h
misc/dumpe2fs.c
misc/mke2fs.8.in
misc/mke2fs.c

index 9b6321d..81c51de 100644 (file)
@@ -515,6 +515,8 @@ void do_show_super_stats(int argc, char *argv[],
                              &first, out);
                print_bg_opts(current_fs, i, EXT2_BG_BLOCK_UNINIT, "Block not init",
                              &first, out);
+               print_bg_opts(current_fs, i, EXT2_BG_IOPS, "IOPS",
+                             &first, out);
                if (gdt_csum) {
                        fprintf(out, "%sChecksum 0x%04x",
                                first ? "           [":", ", ext2fs_bg_checksum(current_fs, i));
index 0b74aea..c13927c 100644 (file)
@@ -162,6 +162,10 @@ static void print_super_flags(struct ext2_super_block * s, FILE *f)
                fputs("test_filesystem ", f);
                flags_found++;
        }
+       if (s->s_flags & EXT2_FLAGS_HAS_IOPS) {
+               fputs("iops ", f);
+               flags_found++;
+       }
        if (flags_found)
                fputs("\n", f);
        else
index fb69e96..ea26d35 100644 (file)
@@ -223,6 +223,7 @@ struct ext4_group_desc
 #define EXT2_BG_INODE_UNINIT   0x0001 /* Inode table/bitmap not initialized */
 #define EXT2_BG_BLOCK_UNINIT   0x0002 /* Block bitmap not initialized */
 #define EXT2_BG_INODE_ZEROED   0x0004 /* On-disk itable initialized to zero */
+#define EXT2_BG_IOPS           0x0010 /* In IOPS/fast storage */
 
 /*
  * Data structures used by the directory indexing feature
@@ -572,6 +573,7 @@ struct ext2_inode *EXT2_INODE(struct ext2_inode_large *large_inode)
 #define EXT2_FLAGS_IS_SNAPSHOT         0x0010  /* This is a snapshot image */
 #define EXT2_FLAGS_FIX_SNAPSHOT                0x0020  /* Snapshot inodes corrupted */
 #define EXT2_FLAGS_FIX_EXCLUDE         0x0040  /* Exclude bitmaps corrupted */
+#define EXT2_FLAGS_HAS_IOPS            0x0080  /* has IOPS storage */
 
 /*
  * Mount flags
index 7c080ed..c6e43d3 100644 (file)
@@ -131,6 +131,8 @@ static void print_bg_opts(ext2_filsys fs, dgrp_t i)
                     &first);
        print_bg_opt(bg_flags, EXT2_BG_INODE_ZEROED, "ITABLE_ZEROED",
                     &first);
+       print_bg_opt(bg_flags, EXT2_BG_IOPS, "IOPS",
+                    &first);
        if (!first)
                fputc(']', stdout);
        fputc('\n', stdout);
index 30f97bb..2d1bc82 100644 (file)
@@ -435,6 +435,14 @@ effect only if the
 feature is set.   The default quota types to be initialized if this
 option is not specified is both user and group quotas.  If the project
 feature is enabled that project quotas will be initialized as well.
+.TP
+.BI iops= <size_range>[:<size_range>][...]
+Specify IOPS block group size range like:
+.B iops=0-1024G:4096-8192G
+So the file system can get the knowledge that which block groups to be accessed
+are on a relatively faster storage and allow the kernel block allocator to
+optimize metadata allocations onto high-IOPS storage for a hybrid flash/HDD
+devices for better performance.
 .RE
 .TP
 .B \-F
index c69efe3..6180382 100644 (file)
@@ -103,6 +103,10 @@ static __u64       offset;
 static blk64_t journal_location = ~0LL;
 static int     proceed_delay = -1;
 static blk64_t dev_size;
+blk64_t                iops_array[64];
+unsigned int   iops_size = sizeof(iops_array);
+unsigned int   iops_count = 0;
+blk64_t                *iops_range = iops_array;
 
 static struct ext2_super_block fs_param;
 static __u32 zero_buf[4];
@@ -742,6 +746,54 @@ static int set_os(struct ext2_super_block *sb, char *os)
        return 1;
 }
 
+static int parse_range(char *p_start, char *p_end, char *p_hyphen)
+{
+       blk64_t start, end;
+       blk64_t *new_array;
+
+       /**
+        * e.g  0-1024G
+        *      ^      ^
+        *      |      |
+        *   p_start  p_end
+        */
+       end = parse_num_blocks(p_hyphen + 1, -1);
+
+       if (!isdigit(*(p_end - 1)) && isdigit(*(p_hyphen -1))) {
+               /* copy G/M/K unit to start value */
+               *p_hyphen = *(p_end - 1);
+               p_hyphen++;
+       }
+       *p_hyphen = 0;
+
+       start = parse_num_blocks(p_start, -1);
+
+       /* add to iops_range */
+       if (iops_count == iops_size) {
+               iops_size <<= 1;
+               if (iops_size == 0) {
+                       iops_size = iops_count;
+                       return -E2BIG;
+               }
+               if (iops_range == iops_array)
+                       new_array = malloc(iops_size * sizeof(blk64_t));
+               else
+                       new_array = realloc(iops_range,
+                                           iops_size * sizeof(blk64_t));
+               if (!new_array) {
+                       iops_size >>= 1;
+                       return -ENOMEM;
+               } else {
+                       iops_range = new_array;
+               }
+       }
+
+       iops_range[iops_count++] = start;
+       iops_range[iops_count++] = end;
+
+       return 0;
+}
+
 #define PATH_SET "PATH=/sbin"
 
 static void parse_extended_opts(struct ext2_super_block *param,
@@ -1059,6 +1111,62 @@ static void parse_extended_opts(struct ext2_super_block *param,
                                r_usage++;
                                continue;
                        }
+               } else if (!strcmp(token, "iops")) {
+                       char *p_colon, *p_hyphen;
+                       blk64_t start, end;
+
+                       /* example: iops=0-1024G:4096-8192G */
+
+                       if (!arg) {
+                               r_usage++;
+                               badopt = token;
+                               continue;
+                       }
+                       p_colon = strchr(arg, ':');
+                       while (p_colon != NULL) {
+                               *p_colon = 0;
+
+                               p_hyphen = strchr(arg, '-');
+                               if (p_hyphen == NULL) {
+                                       fprintf(stderr,
+                                               _("error: parse iops %s\n"),
+                                               arg);
+                                       r_usage++;
+                                       badopt = token;
+                                       break;
+                               }
+
+                               ret = parse_range(arg, p_colon, p_hyphen);
+                               if (ret < 0) {
+                                       fprintf(stderr,
+                                               _("error: parse iops %s:%d\n"),
+                                               arg, ret);
+                                       r_usage++;
+                                       badopt = token;
+                                       break;
+                               }
+
+                               arg = p_colon + 1;
+                               p_colon = strchr(arg, ':');
+                       }
+                       p_hyphen = strchr(arg, '-');
+                       if (p_hyphen == NULL) {
+                               fprintf(stderr,
+                                       _("error: parse iops %s\n"), arg);
+                               r_usage++;
+                               badopt = token;
+                               continue;
+                       }
+
+                       ret = parse_range(arg, arg + strlen(arg), p_hyphen);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       _("error: parse iops %s:%d\n"),
+                                       arg, ret);
+                               r_usage++;
+                               badopt = token;
+                               continue;
+                       }
                } else {
                        r_usage++;
                        badopt = token;
@@ -1085,10 +1193,13 @@ static void parse_extended_opts(struct ext2_super_block *param,
                        "\tnodiscard\n"
                        "\tencoding=<encoding>\n"
                        "\tencoding_flags=<flags>\n"
+                       "\tiops=<iops storage size range>\n"
                        "\tquotatype=<quota type(s) to be enabled>\n"
                        "\tassume_storage_prezeroed=<0 to disable, 1 to enable>\n\n"),
                        badopt ? badopt : "");
                free(buf);
+               if (iops_range != iops_array)
+                       free(iops_range);
                exit(1);
        }
        if (param->s_raid_stride &&
@@ -2973,6 +3084,29 @@ try_user:
        return 0;
 }
 
+static void ext2fs_set_iops_group(ext2_filsys fs, blk64_t *array, int count)
+{
+       int i;
+       dgrp_t j, start, end;
+
+       if (!array || !count)
+               return;
+
+       for (i = 0; i < count; i += 2) {
+               start = ext2fs_div64_ceil(ext2fs_div64_ceil(array[i],
+                                                           fs->blocksize),
+                                         EXT2_BLOCKS_PER_GROUP(fs->super));
+               end = ext2fs_div64_ceil(ext2fs_div64_ceil(array[i + 1],
+                                                         fs->blocksize),
+                                       EXT2_BLOCKS_PER_GROUP(fs->super));
+
+               for (j = start; j < end; j++) {
+                       ext2fs_bg_flags_set(fs, j, EXT2_BG_IOPS);
+                       ext2fs_group_desc_csum_set(fs, j);
+               }
+       }
+}
+
 int main (int argc, char *argv[])
 {
        errcode_t       retval = 0;
@@ -3054,6 +3188,16 @@ int main (int argc, char *argv[])
                        _("while setting up superblock"));
                exit(1);
        }
+
+       if (iops_range && iops_count) {
+               ext2fs_set_iops_group(fs, iops_range, iops_count);
+               fs->super->s_flags |= EXT2_FLAGS_HAS_IOPS;
+               ext2fs_mark_super_dirty(fs);
+
+               if (iops_range != iops_array)
+                       free(iops_range);
+       }
+
        fs->progress_ops = &ext2fs_numeric_progress_ops;
 
        /* Set the error behavior */