Merge "Pin block bitmaps in RAM based on mount option"
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;