Patchwork [3/3] dx read-ahead: Map blocks with a single semaphore lock

login
register
mail settings
Submitter Bernd Schubert
Date June 20, 2011, 8:29 p.m.
Message ID <20110620202900.2473133.26051.stgit@localhost.localdomain>
Download mbox | patch
Permalink /patch/101201/
State New
Headers show

Comments

Bernd Schubert - June 20, 2011, 8:29 p.m.
Map all ra-blocks using a single down_read(&EXT4_I(inode)->i_data_sem
as Andreas suggested.

Signed-off-by: Bernd Schubert <bernd.schubert@itwm.fraunhofer.de>
---
 fs/ext4/ext4.h  |    2 +
 fs/ext4/inode.c |   93 +++++++++++++++++++++++++++++++++++++++++++++++--------
 fs/ext4/namei.c |   23 +++++++++-----
 3 files changed, 96 insertions(+), 22 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 997323a..213ac7c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1804,7 +1804,7 @@  struct buffer_head *ext4_getblk(handle_t *, struct inode *,
 						ext4_lblk_t, int, int *);
 struct buffer_head *ext4_bread(handle_t *, struct inode *,
 						ext4_lblk_t, int, int *);
-int ext4_bread_ra(struct inode *inode, ext4_lblk_t block);
+void ext4_bread_ra(struct inode *inode, ext4_lblk_t blocks[], int nblocks);
 int ext4_get_block(struct inode *inode, sector_t iblock,
 				struct buffer_head *bh_result, int create);
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 938fb6c..5b325c0 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1382,6 +1382,36 @@  int ext4_map_blocks(handle_t *handle, struct inode *inode,
 	return retval;
 }
 
+
+/*
+ * Simplified version to map blocks for read-aheads. We only try
+ * to map existing blocks
+ * NOTE: Should be called with down_read(&EXT4_I(inode)->i_data_sem)
+ */
+int ext4_ra_map_blocks(struct inode *inode, struct ext4_map_blocks *map)
+{
+	int retval;
+
+	map->m_flags = 0;
+	ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u,"
+		  "logical block %lu\n", inode->i_ino, flags, map->m_len,
+		  (unsigned long) map->m_lblk);
+
+	if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+		retval = ext4_ext_map_blocks(NULL, inode, map, 0);
+	} else {
+		retval = ext4_ind_map_blocks(NULL, inode, map, 0);
+	}
+
+	if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
+		int ret = check_block_validity(inode, map);
+		if (ret != 0)
+			return ret;
+	}
+
+	return retval;
+}
+
 /* Maximum number of blocks we map for direct IO at once. */
 #define DIO_MAX_BLOCKS 4096
 
@@ -1491,6 +1521,34 @@  struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
 }
 
 /*
+ * Get an array of buffer heads for read-head.
+ * Blocks that cannot be mapped will be filled into bhs as NULL. The caller
+ * needs to check for that.
+ */
+void ext4_ra_getblks(struct buffer_head *bhs[], struct inode *inode,
+				ext4_lblk_t *blocks, int nblocks)
+{
+	struct ext4_map_blocks map;
+	int err;
+	int i;
+
+	down_read((&EXT4_I(inode)->i_data_sem));
+	for(i = 0; i < nblocks; i++) {
+		map.m_lblk = blocks[i];
+		map.m_len = 1;
+		err = ext4_ra_map_blocks(inode, &map);
+
+		if (err <= 0) {
+			bhs[i] = NULL;
+			continue;
+		}
+
+		bhs[i] = sb_getblk(inode->i_sb, map.m_pblk);
+	}
+	up_read((&EXT4_I(inode)->i_data_sem));
+}
+
+/*
   * Synchronous read of blocks
   */
 struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
@@ -1514,26 +1572,35 @@  struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
 }
 
 /*
- * Read-ahead blocks
+ * Read-ahead blocks. If something fails we just return silently.
  */
-int ext4_bread_ra(struct inode *inode, ext4_lblk_t block)
+void ext4_bread_ra(struct inode *inode, ext4_lblk_t blocks[], int nblocks)
 {
-	struct buffer_head *bh;
-	int err;
+	int i;
+	size_t size = sizeof(struct buffer_head *) * nblocks;
+	struct buffer_head **bhs = kmalloc(size, GFP_KERNEL);
 
-	bh = ext4_getblk(NULL, inode, block, 0, &err);
-	if (!bh)
-		return -1;
+	if (!bhs)
+		return; /* out of memory */
+
+	ext4_ra_getblks(bhs, inode, blocks, nblocks);
+
+	for (i = 0; i < nblocks; i++) {
+		struct buffer_head *bh = bhs[i];
 
-	if (buffer_uptodate(bh)) {
+		if (!bh)
+			continue;
+
+		if (buffer_uptodate(bh)) {
+			brelse(bh);
+			continue;
+		}
+	
+		ll_rw_block(READA, 1, &bh);
 		brelse(bh);
-		return 0;
 	}
 
-	ll_rw_block(READA, 1, &bh);
-
-	brelse(bh);
-	return 0;
+	kfree(bhs);
 }
 
 
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 9643722..34f6f90 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -51,6 +51,7 @@ 
 
 #define NAMEI_RA_DX_BLOCKS  32 /* Better use BH_LRU_SIZE? */
 
+
 static struct buffer_head *ext4_append(handle_t *handle,
 					struct inode *inode,
 					ext4_lblk_t *block, int *err)
@@ -336,18 +337,25 @@  struct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir,
 #endif /* DX_DEBUG */
 
 /*
- * Read ahead directory index blocks
+ * Read ahead directory index blocks. Quit silently on errors.
  */
 static void dx_ra_blocks(struct inode *dir, struct dx_entry *entries,
 			 struct dx_entry *at)
 {
-	int i, err = 0;
+	int i;
 	struct dx_entry *first_ra_entry = entries + 1;
 	unsigned num_entries = dx_get_count(entries) - 1;
+	
+	size_t size = sizeof(ext4_lblk_t) * NAMEI_RA_DX_BLOCKS;
+	ext4_lblk_t *blocks = kmalloc(size, GFP_KERNEL);
+	
+	if (!blocks)
+		return; /* out of memory */
 
 	if (num_entries < 2 || num_entries > dx_get_limit(entries)) {
 		dxtrace(printk("dx read-ahead: invalid number of entries:%d\n",
 			       num_entries));
+		kfree(blocks);
 		return;
 	}
 
@@ -370,13 +378,12 @@  static void dx_ra_blocks(struct inode *dir, struct dx_entry *entries,
 	dxtrace(printk("dx read-ahead: %d entries in dir-ino %lu \n",
 			num_entries, dir->i_ino));
 
-	i = 0;
-	do {
-		struct dx_entry *entry = first_ra_entry + i;
+	for(i = 0; i < num_entries; i++)
+		blocks[i] = dx_get_block(first_ra_entry + i);
+
+	ext4_bread_ra(dir, blocks, num_entries);
 
-		err = ext4_bread_ra(dir, dx_get_block(entry));
-		i++;
-	 } while (i < num_entries && !err);
+	kfree(blocks);
 }
 
 /*