spin_unlock(sb_bgl_lock(sbi, block_group));
        percpu_counter_add(&sbi->s_freeblocks_counter, count);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks += count;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        /* We dirtied the bitmap block */
        BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
        err = ext4_journal_dirty_metadata(handle, bitmap_bh);
        spin_unlock(sb_bgl_lock(sbi, group_no));
        percpu_counter_sub(&sbi->s_freeblocks_counter, num);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi, group_no);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks -= num;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        BUFFER_TRACE(gdp_bh, "journal_dirty_metadata for group descriptor");
        err = ext4_journal_dirty_metadata(handle, gdp_bh);
        if (!fatal)
 
        __u32   bg_reserved2[3];
 };
 
+/*
+ * Structure of a flex block group info
+ */
+
+struct flex_groups {
+       __u32 free_inodes;
+       __u32 free_blocks;
+};
+
 #define EXT4_BG_INODE_UNINIT   0x0001 /* Inode table/bitmap not in use */
 #define EXT4_BG_BLOCK_UNINIT   0x0002 /* Block bitmap not in use */
 #define EXT4_BG_INODE_ZEROED   0x0004 /* On-disk itable initialized to zero */
        __le16  s_mmp_interval;         /* # seconds to wait in MMP checking */
        __le64  s_mmp_block;            /* Block for multi-mount protection */
        __le32  s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
-       __u32   s_reserved[163];        /* Padding to the end of the block */
+       __u8    s_log_groups_per_flex;  /* FLEX_BG group size */
+       __u8    s_reserved_char_pad2;
+       __le16  s_reserved_pad;
+       __u32   s_reserved[162];        /* Padding to the end of the block */
 };
 
 #ifdef __KERNEL__
 }
 
 
+static inline ext4_group_t ext4_flex_group(struct ext4_sb_info *sbi,
+                                            ext4_group_t block_group)
+{
+       return block_group >> sbi->s_log_groups_per_flex;
+}
+
+static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
+{
+       return 1 << sbi->s_log_groups_per_flex;
+}
+
 #define ext4_std_error(sb, errno)                              \
 do {                                                           \
        if ((errno))                                            \
 
 
        /* locality groups */
        struct ext4_locality_group *s_locality_groups;
+
+       unsigned int s_log_groups_per_flex;
+       struct flex_groups *s_flex_groups;
 };
 
 #endif /* _EXT4_SB */
 
        struct ext4_super_block * es;
        struct ext4_sb_info *sbi;
        int fatal = 0, err;
+       ext4_group_t flex_group;
 
        if (atomic_read(&inode->i_count) > 1) {
                printk ("ext4_free_inode: inode has count=%d\n",
                        if (is_directory)
                                percpu_counter_dec(&sbi->s_dirs_counter);
 
+                       if (sbi->s_log_groups_per_flex) {
+                               flex_group = ext4_flex_group(sbi, block_group);
+                               spin_lock(sb_bgl_lock(sbi, flex_group));
+                               sbi->s_flex_groups[flex_group].free_inodes++;
+                               spin_unlock(sb_bgl_lock(sbi, flex_group));
+                       }
                }
                BUFFER_TRACE(bh2, "call ext4_journal_dirty_metadata");
                err = ext4_journal_dirty_metadata(handle, bh2);
        return ret;
 }
 
+#define free_block_ratio 10
+
+static int find_group_flex(struct super_block *sb, struct inode *parent,
+                          ext4_group_t *best_group)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct ext4_group_desc *desc;
+       struct buffer_head *bh;
+       struct flex_groups *flex_group = sbi->s_flex_groups;
+       ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
+       ext4_group_t parent_fbg_group = ext4_flex_group(sbi, parent_group);
+       ext4_group_t ngroups = sbi->s_groups_count;
+       int flex_size = ext4_flex_bg_size(sbi);
+       ext4_group_t best_flex = parent_fbg_group;
+       int blocks_per_flex = sbi->s_blocks_per_group * flex_size;
+       int flexbg_free_blocks;
+       int flex_freeb_ratio;
+       ext4_group_t n_fbg_groups;
+       ext4_group_t i;
+
+       n_fbg_groups = (sbi->s_groups_count + flex_size - 1) >>
+               sbi->s_log_groups_per_flex;
+
+find_close_to_parent:
+       flexbg_free_blocks = flex_group[best_flex].free_blocks;
+       flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex;
+       if (flex_group[best_flex].free_inodes &&
+           flex_freeb_ratio > free_block_ratio)
+               goto found_flexbg;
+
+       if (best_flex && best_flex == parent_fbg_group) {
+               best_flex--;
+               goto find_close_to_parent;
+       }
+
+       for (i = 0; i < n_fbg_groups; i++) {
+               if (i == parent_fbg_group || i == parent_fbg_group - 1)
+                       continue;
+
+               flexbg_free_blocks = flex_group[i].free_blocks;
+               flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex;
+
+               if (flex_freeb_ratio > free_block_ratio &&
+                   flex_group[i].free_inodes) {
+                       best_flex = i;
+                       goto found_flexbg;
+               }
+
+               if (best_flex < 0 ||
+                   (flex_group[i].free_blocks >
+                    flex_group[best_flex].free_blocks &&
+                    flex_group[i].free_inodes))
+                       best_flex = i;
+       }
+
+       if (!flex_group[best_flex].free_inodes ||
+           !flex_group[best_flex].free_blocks)
+               return -1;
+
+found_flexbg:
+       for (i = best_flex * flex_size; i < ngroups &&
+                    i < (best_flex + 1) * flex_size; i++) {
+               desc = ext4_get_group_desc(sb, i, &bh);
+               if (le16_to_cpu(desc->bg_free_inodes_count)) {
+                       *best_group = i;
+                       goto out;
+               }
+       }
+
+       return -1;
+out:
+       return 0;
+}
+
 /*
  * Orlov's allocator for directories.
  *
        struct inode *ret;
        ext4_group_t i;
        int free = 0;
+       ext4_group_t flex_group;
 
        /* Cannot create files in a deleted directory */
        if (!dir || !dir->i_nlink)
 
        sbi = EXT4_SB(sb);
        es = sbi->s_es;
+
+       if (sbi->s_log_groups_per_flex) {
+               ret2 = find_group_flex(sb, dir, &group);
+               goto got_group;
+       }
+
        if (S_ISDIR(mode)) {
                if (test_opt (sb, OLDALLOC))
                        ret2 = find_group_dir(sb, dir, &group);
        } else
                ret2 = find_group_other(sb, dir, &group);
 
+got_group:
        err = -ENOSPC;
        if (ret2 == -1)
                goto out;
                percpu_counter_inc(&sbi->s_dirs_counter);
        sb->s_dirt = 1;
 
+       if (sbi->s_log_groups_per_flex) {
+               flex_group = ext4_flex_group(sbi, group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_inodes--;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        inode->i_uid = current->fsuid;
        if (test_opt (sb, GRPID))
                inode->i_gid = dir->i_gid;
 
        spin_unlock(sb_bgl_lock(sbi, ac->ac_b_ex.fe_group));
        percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi,
+                                                         ac->ac_b_ex.fe_group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks -= ac->ac_b_ex.fe_len;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        err = ext4_journal_dirty_metadata(handle, bitmap_bh);
        if (err)
                goto out_err;
        spin_unlock(sb_bgl_lock(sbi, block_group));
        percpu_counter_add(&sbi->s_freeblocks_counter, count);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks += count;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        ext4_mb_release_desc(&e4b);
 
        *freed += count;
 
        for (i = 0; i < sbi->s_gdb_count; i++)
                brelse(sbi->s_group_desc[i]);
        kfree(sbi->s_group_desc);
+       kfree(sbi->s_flex_groups);
        percpu_counter_destroy(&sbi->s_freeblocks_counter);
        percpu_counter_destroy(&sbi->s_freeinodes_counter);
        percpu_counter_destroy(&sbi->s_dirs_counter);
        return res;
 }
 
+static int ext4_fill_flex_info(struct super_block *sb)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct ext4_group_desc *gdp = NULL;
+       struct buffer_head *bh;
+       ext4_group_t flex_group_count;
+       ext4_group_t flex_group;
+       int groups_per_flex = 0;
+       __u64 block_bitmap = 0;
+       int i;
+
+       if (!sbi->s_es->s_log_groups_per_flex) {
+               sbi->s_log_groups_per_flex = 0;
+               return 1;
+       }
+
+       sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
+       groups_per_flex = 1 << sbi->s_log_groups_per_flex;
+
+       flex_group_count = (sbi->s_groups_count + groups_per_flex - 1) /
+               groups_per_flex;
+       sbi->s_flex_groups = kmalloc(flex_group_count *
+                                    sizeof(struct flex_groups), GFP_KERNEL);
+       if (sbi->s_flex_groups == NULL) {
+               printk(KERN_ERR "EXT4-fs: not enough memory\n");
+               goto failed;
+       }
+       memset(sbi->s_flex_groups, 0, flex_group_count *
+              sizeof(struct flex_groups));
+
+       gdp = ext4_get_group_desc(sb, 1, &bh);
+       block_bitmap = ext4_block_bitmap(sb, gdp) - 1;
+
+       for (i = 0; i < sbi->s_groups_count; i++) {
+               gdp = ext4_get_group_desc(sb, i, &bh);
+
+               flex_group = ext4_flex_group(sbi, i);
+               sbi->s_flex_groups[flex_group].free_inodes +=
+                       le16_to_cpu(gdp->bg_free_inodes_count);
+               sbi->s_flex_groups[flex_group].free_blocks +=
+                       le16_to_cpu(gdp->bg_free_blocks_count);
+       }
+
+       return 1;
+failed:
+       return 0;
+}
+
 __le16 ext4_group_desc_csum(struct ext4_sb_info *sbi, __u32 block_group,
                            struct ext4_group_desc *gdp)
 {
                printk(KERN_ERR "EXT4-fs: group descriptors corrupted!\n");
                goto failed_mount2;
        }
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG))
+               if (!ext4_fill_flex_info(sb)) {
+                       printk(KERN_ERR
+                              "EXT4-fs: unable to initialize "
+                              "flex_bg meta info!\n");
+                       goto failed_mount2;
+               }
+
        sbi->s_gdb_count = db_count;
        get_random_bytes(&sbi->s_next_generation, sizeof(u32));
        spin_lock_init(&sbi->s_next_gen_lock);