ext4: make ext4_block_in_group() much more efficient
authorLukas Czerner <lczerner@redhat.com>
Thu, 4 Apr 2013 02:12:52 +0000 (22:12 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 4 Apr 2013 02:12:52 +0000 (22:12 -0400)
Currently in when getting the block group number for a particular
block in ext4_block_in_group() we're using
ext4_get_group_no_and_offset() which uses do_div() to get the block
group and the remainer which is offset within the group.

We don't need all of that in ext4_block_in_group() as we only need to
figure out the group number.

This commit changes ext4_block_in_group() to calculate group number
directly. This shows as a big improvement with regards to cpu
utilization. Measuring fallocate -l 15T on fresh file system with perf
showed that 23% of cpu time was spend in the
ext4_get_group_no_and_offset(). With this change it completely
disappears from the list only bumping the occurrence of
ext4_init_block_bitmap() which is the biggest user of
ext4_block_in_group() by 4%. As the result of this change on my system
the fallocate call was approx. 10% faster.

However since there is '-g' option in mkfs which allow us setting
different groups size (mostly for developers) I've introduced new per
file system flag whether we have a standard block group size or
not. The flag is used to determine whether we can use the bit shift
optimization or not.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/balloc.c
fs/ext4/ext4.h
fs/ext4/super.c

index 92e68b33fffde26bd98c899010589ffaea70f699..d6babf94907e02d0b3a2f67e3cd96264b26e0c74 100644 (file)
@@ -49,14 +49,24 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr,
 
 }
 
-static int ext4_block_in_group(struct super_block *sb, ext4_fsblk_t block,
-                       ext4_group_t block_group)
+/*
+ * Check whether the 'block' lives within the 'block_group'. Returns 1 if so
+ * and 0 otherwise.
+ */
+static inline int ext4_block_in_group(struct super_block *sb,
+                                     ext4_fsblk_t block,
+                                     ext4_group_t block_group)
 {
        ext4_group_t actual_group;
-       ext4_get_group_no_and_offset(sb, block, &actual_group, NULL);
-       if (actual_group == block_group)
-               return 1;
-       return 0;
+
+       if (test_opt2(sb, STD_GROUP_SIZE))
+               actual_group =
+                       (le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block) +
+                       block) >>
+                       (EXT4_BLOCK_SIZE_BITS(sb) + EXT4_CLUSTER_BITS(sb) + 3);
+       else
+               ext4_get_group_no_and_offset(sb, block, &actual_group, NULL);
+       return (actual_group == block_group) ? 1 : 0;
 }
 
 /* Return the number of clusters used for file system metadata; this
index 73f3e60f70789bd0c2627f7998e6f77666256c5a..cc58aa8e98692cdb4b5f75118aea05f04587cd58 100644 (file)
@@ -949,7 +949,7 @@ struct ext4_inode_info {
 #define EXT2_FLAGS_TEST_FILESYS                0x0004  /* to test development code */
 
 /*
- * Mount flags
+ * Mount flags set via mount options or defaults
  */
 #define EXT4_MOUNT_GRPID               0x00004 /* Create files with directory's group */
 #define EXT4_MOUNT_DEBUG               0x00008 /* Some debugging messages */
@@ -981,8 +981,16 @@ struct ext4_inode_info {
 #define EXT4_MOUNT_DISCARD             0x40000000 /* Issue DISCARD requests */
 #define EXT4_MOUNT_INIT_INODE_TABLE    0x80000000 /* Initialize uninitialized itables */
 
+/*
+ * Mount flags set either automatically (could not be set by mount option)
+ * based on per file system feature or property or in special cases such as
+ * distinguishing between explicit mount option definition and default.
+ */
 #define EXT4_MOUNT2_EXPLICIT_DELALLOC  0x00000001 /* User explicitly
                                                      specified delalloc */
+#define EXT4_MOUNT2_STD_GROUP_SIZE     0x00000002 /* We have standard group
+                                                     size of blocksize * 8
+                                                     blocks */
 
 #define clear_opt(sb, opt)             EXT4_SB(sb)->s_mount_opt &= \
                                                ~EXT4_MOUNT_##opt
index febbe0e180240da98e905b410fbcb620aec5009d..525beb6e3e1e81604f22399cf7818a7dc6b79d50 100644 (file)
@@ -3529,6 +3529,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_addr_per_block_bits = ilog2(EXT4_ADDR_PER_BLOCK(sb));
        sbi->s_desc_per_block_bits = ilog2(EXT4_DESC_PER_BLOCK(sb));
 
+       /* Do we have standard group size of blocksize * 8 blocks ? */
+       if (sbi->s_blocks_per_group == blocksize << 3)
+               set_opt2(sb, STD_GROUP_SIZE);
+
        for (i = 0; i < 4; i++)
                sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]);
        sbi->s_def_hash_version = es->s_def_hash_version;