Pin block bitmaps in RAM based on mount option

This kernel patch was given to me by Ted Tso. This will
enable adding a mount option for the hard disk which will
then read all of the block bitmaps from the hard disk at mount
time and store them in RAM. This will avoid any slow calls to
fallocate since it won't need to seek around the disk to find free
blocks because all the data for that will already be in RAM.

The mount option for this is: pin_block_bitmaps

Change-Id: Id1682825c48267292f5ec635dd44becd0a92312c
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 0084ef9..3c8fc16 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -928,6 +928,7 @@
 #define EXT4_MOUNT_POSIX_ACL		0x08000	/* POSIX Access Control Lists */
 #define EXT4_MOUNT_NO_AUTO_DA_ALLOC	0x10000	/* No auto delalloc mapping */
 #define EXT4_MOUNT_BARRIER		0x20000 /* Use block barriers */
+#define EXT4_MOUNT_PIN_BLOCK_BITMAPS	0x40000 /* Pin block bitmaps */
 #define EXT4_MOUNT_QUOTA		0x80000 /* Some quota option set */
 #define EXT4_MOUNT_USRQUOTA		0x100000 /* "old" user quota */
 #define EXT4_MOUNT_GRPQUOTA		0x200000 /* "old" group quota */
@@ -2137,6 +2138,7 @@
 #ifdef DOUBLE_CHECK
 	void            *bb_bitmap;
 #endif
+	struct buffer_head *bb_bh;
 	struct rw_semaphore alloc_sem;
 	ext4_grpblk_t	bb_counters[];	/* Nr of free power-of-two-block
 					 * regions, index is order.
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 4e99e1d..cebbab3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -753,6 +753,33 @@
 }
 
 /*
+ * Manage pinning (and unpinning) block bitmaps
+ */
+static void ext4_pin_block_bitmaps(struct super_block *sb)
+{
+	ext4_group_t i, ngroups = ext4_get_groups_count(sb);
+	struct ext4_group_info *grinfo;
+
+	for (i = 0; i < ngroups; i++) {
+		grinfo = ext4_get_group_info(sb, i);
+		if (!grinfo->bb_bh)
+			grinfo->bb_bh = ext4_read_block_bitmap(sb, i);
+	}
+}
+
+static void ext4_unpin_block_bitmaps(struct super_block *sb)
+{
+	ext4_group_t i, ngroups = ext4_get_groups_count(sb);
+	struct ext4_group_info *grinfo;
+
+	for (i = 0; i < ngroups; i++) {
+		grinfo = ext4_get_group_info(sb, i);
+		brelse(grinfo->bb_bh);
+		grinfo->bb_bh = NULL;
+	}
+}
+
+/*
  * Release the journal device
  */
 static int ext4_blkdev_put(struct block_device *bdev)
@@ -821,6 +848,7 @@
 
 	del_timer(&sbi->s_err_report);
 	ext4_release_system_zone(sb);
+	ext4_unpin_block_bitmaps(sb);
 	ext4_mb_release(sb);
 	ext4_ext_release(sb);
 	ext4_xattr_put_super(sb);
@@ -1164,6 +1192,9 @@
 	if (sbi->no_hide_stale_gid != -1)
 		seq_printf(seq, ",nohide_stale_gid=%u", sbi->no_hide_stale_gid);
 
+	if (test_opt(sb, PIN_BLOCK_BITMAPS))
+		seq_puts(seq, ",pin_block_bitmaps");
+
 	ext4_show_quota_options(seq, sb);
 
 	return 0;
@@ -1339,6 +1370,7 @@
 	Opt_dioread_nolock, Opt_dioread_lock,
 	Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
 	Opt_nohide_stale_gid,
+	Opt_pin_block_bitmaps, Opt_nopin_block_bitmaps,
 };
 
 static const match_table_t tokens = {
@@ -1415,6 +1447,8 @@
 	{Opt_init_itable, "init_itable"},
 	{Opt_noinit_itable, "noinit_itable"},
 	{Opt_nohide_stale_gid, "nohide_stale_gid=%u"},
+	{Opt_pin_block_bitmaps, "pin_block_bitmaps"},
+	{Opt_nopin_block_bitmaps, "nopin_block_bitmaps"},
 	{Opt_err, NULL},
 };
 
@@ -1917,6 +1951,12 @@
 		case Opt_noinit_itable:
 			clear_opt(sb, INIT_INODE_TABLE);
 			break;
+		case Opt_pin_block_bitmaps:
+			set_opt(sb, PIN_BLOCK_BITMAPS);
+			break;
+		case Opt_nopin_block_bitmaps:
+			clear_opt(sb, PIN_BLOCK_BITMAPS);
+			break;
 		default:
 			ext4_msg(sb, KERN_ERR,
 			       "Unrecognized mount option \"%s\" "
@@ -3937,6 +3977,8 @@
 	} else
 		descr = "out journal";
 
+	if (test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_pin_block_bitmaps(sb);
 	ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
 		 "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
 		 *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
@@ -4679,6 +4721,11 @@
 	if (enable_quota)
 		dquot_resume(sb, -1);
 
+	if (test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_pin_block_bitmaps(sb);
+	if (!test_opt(sb, PIN_BLOCK_BITMAPS))
+		ext4_unpin_block_bitmaps(sb);
+
 	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
 	kfree(orig_data);
 	return 0;