diff options
author | Bob Peterson <rpeterso@redhat.com> | 2011-10-27 12:16:06 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2011-11-08 04:52:12 -0500 |
commit | dfe4d34b39b80faff52489f950a18523da7581bf (patch) | |
tree | be478e2c4988612eef88a1669f774dd8f9f9b8af /fs/gfs2/dir.c | |
parent | 20ed0535d35b74c9e4fa5777766d6e836fe3c90c (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>
Diffstat (limited to 'fs/gfs2/dir.c')
-rw-r--r-- | fs/gfs2/dir.c | 56 |
1 files changed, 53 insertions, 3 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. */ | ||
1387 | static 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 | ||
1390 | static int dir_e_read(struct inode *inode, u64 *offset, void *opaque, | 1436 | static 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 | ||
1425 | int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, | 1475 | int 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); |