diff options
author | Tahsin Erdogan <tahsin@google.com> | 2017-08-06 00:07:01 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2017-08-06 00:07:01 -0400 |
commit | 9699d4f91d9bd2f70dcc37afe3c9f18145ab2dba (patch) | |
tree | 8fc510c6e2da691b84598b8b2cb13aeb92d00288 | |
parent | ec00022030da5761518476096626338bd67df57a (diff) |
ext4: make xattr inode reads faster
ext4_xattr_inode_read() currently reads each block sequentially while
waiting for io operation to complete before moving on to the next
block. This prevents request merging in block layer.
Add a ext4_bread_batch() function that starts reads for all blocks
then optionally waits for them to complete. A similar logic is used
in ext4_find_entry(), so update that code to use the new function.
Signed-off-by: Tahsin Erdogan <tahsin@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/inode.c | 44 | ||||
-rw-r--r-- | fs/ext4/namei.c | 43 | ||||
-rw-r--r-- | fs/ext4/xattr.c | 51 |
4 files changed, 92 insertions, 48 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index d50229e92c55..a2bb7d2870e4 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2461,6 +2461,8 @@ extern void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid); | |||
2461 | int ext4_inode_is_fast_symlink(struct inode *inode); | 2461 | int ext4_inode_is_fast_symlink(struct inode *inode); |
2462 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); | 2462 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); |
2463 | struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); | 2463 | struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); |
2464 | int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, | ||
2465 | bool wait, struct buffer_head **bhs); | ||
2464 | int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, | 2466 | int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, |
2465 | struct buffer_head *bh_result, int create); | 2467 | struct buffer_head *bh_result, int create); |
2466 | int ext4_get_block(struct inode *inode, sector_t iblock, | 2468 | int ext4_get_block(struct inode *inode, sector_t iblock, |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2e6c02258ee2..56bca45bcdf4 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1015,6 +1015,50 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode, | |||
1015 | return ERR_PTR(-EIO); | 1015 | return ERR_PTR(-EIO); |
1016 | } | 1016 | } |
1017 | 1017 | ||
1018 | /* Read a contiguous batch of blocks. */ | ||
1019 | int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, | ||
1020 | bool wait, struct buffer_head **bhs) | ||
1021 | { | ||
1022 | int i, err; | ||
1023 | |||
1024 | for (i = 0; i < bh_count; i++) { | ||
1025 | bhs[i] = ext4_getblk(NULL, inode, block + i, 0 /* map_flags */); | ||
1026 | if (IS_ERR(bhs[i])) { | ||
1027 | err = PTR_ERR(bhs[i]); | ||
1028 | bh_count = i; | ||
1029 | goto out_brelse; | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | for (i = 0; i < bh_count; i++) | ||
1034 | /* Note that NULL bhs[i] is valid because of holes. */ | ||
1035 | if (bhs[i] && !buffer_uptodate(bhs[i])) | ||
1036 | ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, | ||
1037 | &bhs[i]); | ||
1038 | |||
1039 | if (!wait) | ||
1040 | return 0; | ||
1041 | |||
1042 | for (i = 0; i < bh_count; i++) | ||
1043 | if (bhs[i]) | ||
1044 | wait_on_buffer(bhs[i]); | ||
1045 | |||
1046 | for (i = 0; i < bh_count; i++) { | ||
1047 | if (bhs[i] && !buffer_uptodate(bhs[i])) { | ||
1048 | err = -EIO; | ||
1049 | goto out_brelse; | ||
1050 | } | ||
1051 | } | ||
1052 | return 0; | ||
1053 | |||
1054 | out_brelse: | ||
1055 | for (i = 0; i < bh_count; i++) { | ||
1056 | brelse(bhs[i]); | ||
1057 | bhs[i] = NULL; | ||
1058 | } | ||
1059 | return err; | ||
1060 | } | ||
1061 | |||
1018 | int ext4_walk_page_buffers(handle_t *handle, | 1062 | int ext4_walk_page_buffers(handle_t *handle, |
1019 | struct buffer_head *head, | 1063 | struct buffer_head *head, |
1020 | unsigned from, | 1064 | unsigned from, |
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index cc986b824181..c1cf020d1889 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
@@ -1342,13 +1342,12 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, | |||
1342 | struct super_block *sb; | 1342 | struct super_block *sb; |
1343 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; | 1343 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; |
1344 | struct buffer_head *bh, *ret = NULL; | 1344 | struct buffer_head *bh, *ret = NULL; |
1345 | ext4_lblk_t start, block, b; | 1345 | ext4_lblk_t start, block; |
1346 | const u8 *name = d_name->name; | 1346 | const u8 *name = d_name->name; |
1347 | int ra_max = 0; /* Number of bh's in the readahead | 1347 | size_t ra_max = 0; /* Number of bh's in the readahead |
1348 | buffer, bh_use[] */ | 1348 | buffer, bh_use[] */ |
1349 | int ra_ptr = 0; /* Current index into readahead | 1349 | size_t ra_ptr = 0; /* Current index into readahead |
1350 | buffer */ | 1350 | buffer */ |
1351 | int num = 0; | ||
1352 | ext4_lblk_t nblocks; | 1351 | ext4_lblk_t nblocks; |
1353 | int i, namelen, retval; | 1352 | int i, namelen, retval; |
1354 | struct ext4_filename fname; | 1353 | struct ext4_filename fname; |
@@ -1411,31 +1410,17 @@ restart: | |||
1411 | if (ra_ptr >= ra_max) { | 1410 | if (ra_ptr >= ra_max) { |
1412 | /* Refill the readahead buffer */ | 1411 | /* Refill the readahead buffer */ |
1413 | ra_ptr = 0; | 1412 | ra_ptr = 0; |
1414 | b = block; | 1413 | if (block < start) |
1415 | for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { | 1414 | ra_max = start - block; |
1416 | /* | 1415 | else |
1417 | * Terminate if we reach the end of the | 1416 | ra_max = nblocks - block; |
1418 | * directory and must wrap, or if our | 1417 | ra_max = min(ra_max, ARRAY_SIZE(bh_use)); |
1419 | * search has finished at this block. | 1418 | retval = ext4_bread_batch(dir, block, ra_max, |
1420 | */ | 1419 | false /* wait */, bh_use); |
1421 | if (b >= nblocks || (num && block == start)) { | 1420 | if (retval) { |
1422 | bh_use[ra_max] = NULL; | 1421 | ret = ERR_PTR(retval); |
1423 | break; | 1422 | ra_max = 0; |
1424 | } | 1423 | goto cleanup_and_exit; |
1425 | num++; | ||
1426 | bh = ext4_getblk(NULL, dir, b++, 0); | ||
1427 | if (IS_ERR(bh)) { | ||
1428 | if (ra_max == 0) { | ||
1429 | ret = bh; | ||
1430 | goto cleanup_and_exit; | ||
1431 | } | ||
1432 | break; | ||
1433 | } | ||
1434 | bh_use[ra_max] = bh; | ||
1435 | if (bh) | ||
1436 | ll_rw_block(REQ_OP_READ, | ||
1437 | REQ_META | REQ_PRIO, | ||
1438 | 1, &bh); | ||
1439 | } | 1424 | } |
1440 | } | 1425 | } |
1441 | if ((bh = bh_use[ra_ptr++]) == NULL) | 1426 | if ((bh = bh_use[ra_ptr++]) == NULL) |
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 4025666c5991..5fa912e5d2a6 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c | |||
@@ -317,28 +317,41 @@ static void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash) | |||
317 | */ | 317 | */ |
318 | static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size) | 318 | static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size) |
319 | { | 319 | { |
320 | unsigned long block = 0; | 320 | int blocksize = 1 << ea_inode->i_blkbits; |
321 | struct buffer_head *bh; | 321 | int bh_count = (size + blocksize - 1) >> ea_inode->i_blkbits; |
322 | int blocksize = ea_inode->i_sb->s_blocksize; | 322 | int tail_size = (size % blocksize) ?: blocksize; |
323 | size_t csize, copied = 0; | 323 | struct buffer_head *bhs_inline[8]; |
324 | void *copy_pos = buf; | 324 | struct buffer_head **bhs = bhs_inline; |
325 | 325 | int i, ret; | |
326 | while (copied < size) { | 326 | |
327 | csize = (size - copied) > blocksize ? blocksize : size - copied; | 327 | if (bh_count > ARRAY_SIZE(bhs_inline)) { |
328 | bh = ext4_bread(NULL, ea_inode, block, 0); | 328 | bhs = kmalloc_array(bh_count, sizeof(*bhs), GFP_NOFS); |
329 | if (IS_ERR(bh)) | 329 | if (!bhs) |
330 | return PTR_ERR(bh); | 330 | return -ENOMEM; |
331 | if (!bh) | 331 | } |
332 | return -EFSCORRUPTED; | ||
333 | 332 | ||
334 | memcpy(copy_pos, bh->b_data, csize); | 333 | ret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count, |
335 | brelse(bh); | 334 | true /* wait */, bhs); |
335 | if (ret) | ||
336 | goto free_bhs; | ||
336 | 337 | ||
337 | copy_pos += csize; | 338 | for (i = 0; i < bh_count; i++) { |
338 | block += 1; | 339 | /* There shouldn't be any holes in ea_inode. */ |
339 | copied += csize; | 340 | if (!bhs[i]) { |
341 | ret = -EFSCORRUPTED; | ||
342 | goto put_bhs; | ||
343 | } | ||
344 | memcpy((char *)buf + blocksize * i, bhs[i]->b_data, | ||
345 | i < bh_count - 1 ? blocksize : tail_size); | ||
340 | } | 346 | } |
341 | return 0; | 347 | ret = 0; |
348 | put_bhs: | ||
349 | for (i = 0; i < bh_count; i++) | ||
350 | brelse(bhs[i]); | ||
351 | free_bhs: | ||
352 | if (bhs != bhs_inline) | ||
353 | kfree(bhs); | ||
354 | return ret; | ||
342 | } | 355 | } |
343 | 356 | ||
344 | static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, | 357 | static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, |