aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBob Peterson <rpeterso@redhat.com>2011-10-27 12:16:06 -0400
committerSteven Whitehouse <swhiteho@redhat.com>2011-11-08 04:52:12 -0500
commitdfe4d34b39b80faff52489f950a18523da7581bf (patch)
treebe478e2c4988612eef88a1669f774dd8f9f9b8af
parent20ed0535d35b74c9e4fa5777766d6e836fe3c90c (diff)
GFS2: Add readahead to sequential directory traversal
This patch adds read-ahead capability to GFS2's directory hash table management. It greatly improves performance for some directory operations. For example: In one of my file systems that has 1000 directories, each of which has 1000 files, time to execute a recursive ls (time ls -fR /mnt/gfs2 > /dev/null) was reduced from 2m2.814s on a stock kernel to 0m45.938s. Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r--fs/gfs2/dir.c56
-rw-r--r--fs/gfs2/dir.h2
-rw-r--r--fs/gfs2/export.c3
-rw-r--r--fs/gfs2/file.c2
4 files changed, 57 insertions, 6 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 8ccad2467cb6..91441171bf25 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -76,6 +76,8 @@
76#define IS_LEAF 1 /* Hashed (leaf) directory */ 76#define IS_LEAF 1 /* Hashed (leaf) directory */
77#define IS_DINODE 2 /* Linear (stuffed dinode block) directory */ 77#define IS_DINODE 2 /* Linear (stuffed dinode block) directory */
78 78
79#define MAX_RA_BLOCKS 32 /* max read-ahead blocks */
80
79#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1) 81#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
80#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1)) 82#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
81 83
@@ -1376,6 +1378,50 @@ out:
1376 return error; 1378 return error;
1377} 1379}
1378 1380
1381/* gfs2_dir_readahead - Issue read-ahead requests for leaf blocks.
1382 *
1383 * Note: we can't calculate each index like dir_e_read can because we don't
1384 * have the leaf, and therefore we don't have the depth, and therefore we
1385 * don't have the length. So we have to just read enough ahead to make up
1386 * for the loss of information. */
1387static void gfs2_dir_readahead(struct inode *inode, unsigned hsize, u32 index,
1388 struct file_ra_state *f_ra)
1389{
1390 struct gfs2_inode *ip = GFS2_I(inode);
1391 struct gfs2_glock *gl = ip->i_gl;
1392 struct buffer_head *bh;
1393 u64 blocknr = 0, last;
1394 unsigned count;
1395
1396 /* First check if we've already read-ahead for the whole range. */
1397 if (!f_ra || index + MAX_RA_BLOCKS < f_ra->start)
1398 return;
1399
1400 f_ra->start = max((pgoff_t)index, f_ra->start);
1401 for (count = 0; count < MAX_RA_BLOCKS; count++) {
1402 if (f_ra->start >= hsize) /* if exceeded the hash table */
1403 break;
1404
1405 last = blocknr;
1406 blocknr = be64_to_cpu(ip->i_hash_cache[f_ra->start]);
1407 f_ra->start++;
1408 if (blocknr == last)
1409 continue;
1410
1411 bh = gfs2_getbuf(gl, blocknr, 1);
1412 if (trylock_buffer(bh)) {
1413 if (buffer_uptodate(bh)) {
1414 unlock_buffer(bh);
1415 brelse(bh);
1416 continue;
1417 }
1418 bh->b_end_io = end_buffer_read_sync;
1419 submit_bh(READA | REQ_META, bh);
1420 continue;
1421 }
1422 brelse(bh);
1423 }
1424}
1379 1425
1380/** 1426/**
1381 * dir_e_read - Reads the entries from a directory into a filldir buffer 1427 * dir_e_read - Reads the entries from a directory into a filldir buffer
@@ -1388,7 +1434,7 @@ out:
1388 */ 1434 */
1389 1435
1390static int dir_e_read(struct inode *inode, u64 *offset, void *opaque, 1436static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
1391 filldir_t filldir) 1437 filldir_t filldir, struct file_ra_state *f_ra)
1392{ 1438{
1393 struct gfs2_inode *dip = GFS2_I(inode); 1439 struct gfs2_inode *dip = GFS2_I(inode);
1394 u32 hsize, len = 0; 1440 u32 hsize, len = 0;
@@ -1402,10 +1448,14 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
1402 hash = gfs2_dir_offset2hash(*offset); 1448 hash = gfs2_dir_offset2hash(*offset);
1403 index = hash >> (32 - dip->i_depth); 1449 index = hash >> (32 - dip->i_depth);
1404 1450
1451 if (f_ra && dip->i_hash_cache == NULL)
1452 f_ra->start = 0;
1405 lp = gfs2_dir_get_hash_table(dip); 1453 lp = gfs2_dir_get_hash_table(dip);
1406 if (IS_ERR(lp)) 1454 if (IS_ERR(lp))
1407 return PTR_ERR(lp); 1455 return PTR_ERR(lp);
1408 1456
1457 gfs2_dir_readahead(inode, hsize, index, f_ra);
1458
1409 while (index < hsize) { 1459 while (index < hsize) {
1410 error = gfs2_dir_read_leaf(inode, offset, opaque, filldir, 1460 error = gfs2_dir_read_leaf(inode, offset, opaque, filldir,
1411 &copied, &depth, 1461 &copied, &depth,
@@ -1423,7 +1473,7 @@ static int dir_e_read(struct inode *inode, u64 *offset, void *opaque,
1423} 1473}
1424 1474
1425int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, 1475int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
1426 filldir_t filldir) 1476 filldir_t filldir, struct file_ra_state *f_ra)
1427{ 1477{
1428 struct gfs2_inode *dip = GFS2_I(inode); 1478 struct gfs2_inode *dip = GFS2_I(inode);
1429 struct gfs2_sbd *sdp = GFS2_SB(inode); 1479 struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -1437,7 +1487,7 @@ int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
1437 return 0; 1487 return 0;
1438 1488
1439 if (dip->i_diskflags & GFS2_DIF_EXHASH) 1489 if (dip->i_diskflags & GFS2_DIF_EXHASH)
1440 return dir_e_read(inode, offset, opaque, filldir); 1490 return dir_e_read(inode, offset, opaque, filldir, f_ra);
1441 1491
1442 if (!gfs2_is_stuffed(dip)) { 1492 if (!gfs2_is_stuffed(dip)) {
1443 gfs2_consist_inode(dip); 1493 gfs2_consist_inode(dip);
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h
index ff5772fbf024..98c960beab35 100644
--- a/fs/gfs2/dir.h
+++ b/fs/gfs2/dir.h
@@ -25,7 +25,7 @@ extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
25 const struct gfs2_inode *ip); 25 const struct gfs2_inode *ip);
26extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry); 26extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry);
27extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, 27extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
28 filldir_t filldir); 28 filldir_t filldir, struct file_ra_state *f_ra);
29extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, 29extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
30 const struct gfs2_inode *nip, unsigned int new_type); 30 const struct gfs2_inode *nip, unsigned int new_type);
31 31
diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c
index fe9945f2ff72..70ba891654f8 100644
--- a/fs/gfs2/export.c
+++ b/fs/gfs2/export.c
@@ -99,6 +99,7 @@ static int gfs2_get_name(struct dentry *parent, char *name,
99 struct gfs2_holder gh; 99 struct gfs2_holder gh;
100 u64 offset = 0; 100 u64 offset = 0;
101 int error; 101 int error;
102 struct file_ra_state f_ra = { .start = 0 };
102 103
103 if (!dir) 104 if (!dir)
104 return -EINVAL; 105 return -EINVAL;
@@ -118,7 +119,7 @@ static int gfs2_get_name(struct dentry *parent, char *name,
118 if (error) 119 if (error)
119 return error; 120 return error;
120 121
121 error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir); 122 error = gfs2_dir_read(dir, &offset, &gnfd, get_name_filldir, &f_ra);
122 123
123 gfs2_glock_dq_uninit(&gh); 124 gfs2_glock_dq_uninit(&gh);
124 125
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index ce36a56dfeac..46f6f9ac1ebc 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -105,7 +105,7 @@ static int gfs2_readdir(struct file *file, void *dirent, filldir_t filldir)
105 return error; 105 return error;
106 } 106 }
107 107
108 error = gfs2_dir_read(dir, &offset, dirent, filldir); 108 error = gfs2_dir_read(dir, &offset, dirent, filldir, &file->f_ra);
109 109
110 gfs2_glock_dq_uninit(&d_gh); 110 gfs2_glock_dq_uninit(&d_gh);
111 111