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.
Unified · Split
| @@ -2461,6 +2461,8 @@ extern void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid); · 2460 unmodified lines | ||
| 2461 | 2461 | int ext4_inode_is_fast_symlink(struct inode *inode); |
| 2462 | 2462 | struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); |
| 2463 | 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 | 2466 | int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, |
| 2465 | 2467 | struct buffer_head *bh_result, int create); |
| 2466 | 2468 | int ext4_get_block(struct inode *inode, sector_t iblock, |
| @@ -1015,6 +1015,50 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode, · 1014 unmodified lines | ||
| 1015 | 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 | 1062 | int ext4_walk_page_buffers(handle_t *handle, |
| 1019 | 1063 | struct buffer_head *head, |
| 1020 | 1064 | unsigned from, |
| @@ -1342,13 +1342,12 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, · 1341 unmodified lines | ||
| 1342 | 1342 | struct super_block *sb; |
| 1343 | 1343 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; |
| 1344 | 1344 | struct buffer_head *bh, *ret = NULL; |
| 1345 | − ext4_lblk_t start, block, b; | |
| 1345 | + ext4_lblk_t start, block; | |
| 1346 | 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 | 1348 | buffer, bh_use[] */ |
| 1349 | − int ra_ptr = 0; /* Current index into readahead | |
| 1349 | + size_t ra_ptr = 0; /* Current index into readahead | |
| 1350 | 1350 | buffer */ |
| 1351 | − int num = 0; | |
| 1352 | 1351 | ext4_lblk_t nblocks; |
| 1353 | 1352 | int i, namelen, retval; |
| 1354 | 1353 | struct ext4_filename fname; |
| @@ -1411,31 +1410,17 @@ restart: · 56 unmodified lines | ||
| 1411 | 1410 | if (ra_ptr >= ra_max) { |
| 1412 | 1411 | /* Refill the readahead buffer */ |
| 1413 | 1412 | ra_ptr = 0; |
| 1414 | − b = block; | |
| 1415 | − for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { | |
| 1416 | − /* | |
| 1417 | − * Terminate if we reach the end of the | |
| 1418 | − * directory and must wrap, or if our | |
| 1419 | − * search has finished at this block. | |
| 1420 | − */ | |
| 1421 | − if (b >= nblocks || (num && block == start)) { | |
| 1422 | − bh_use[ra_max] = NULL; | |
| 1423 | − break; | |
| 1424 | − } | |
| 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); | |
| 1413 | + if (block < start) | |
| 1414 | + ra_max = start - block; | |
| 1415 | + else | |
| 1416 | + ra_max = nblocks - block; | |
| 1417 | + ra_max = min(ra_max, ARRAY_SIZE(bh_use)); | |
| 1418 | + retval = ext4_bread_batch(dir, block, ra_max, | |
| 1419 | + false /* wait */, bh_use); | |
| 1420 | + if (retval) { | |
| 1421 | + ret = ERR_PTR(retval); | |
| 1422 | + ra_max = 0; | |
| 1423 | + goto cleanup_and_exit; | |
| 1439 | 1424 | } |
| 1440 | 1425 | } |
| 1441 | 1426 | if ((bh = bh_use[ra_ptr++]) == NULL) |
| @@ -317,28 +317,41 @@ static void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash) · 316 unmodified lines | ||
| 317 | 317 | */ |
| 318 | 318 | static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size) |
| 319 | 319 | { |
| 320 | − unsigned long block = 0; | |
| 321 | − struct buffer_head *bh; | |
| 322 | − int blocksize = ea_inode->i_sb->s_blocksize; | |
| 323 | − size_t csize, copied = 0; | |
| 324 | − void *copy_pos = buf; | |
| 325 | − | |
| 326 | − while (copied < size) { | |
| 327 | − csize = (size - copied) > blocksize ? blocksize : size - copied; | |
| 328 | − bh = ext4_bread(NULL, ea_inode, block, 0); | |
| 329 | − if (IS_ERR(bh)) | |
| 330 | − return PTR_ERR(bh); | |
| 331 | − if (!bh) | |
| 332 | − return -EFSCORRUPTED; | |
| 320 | + int blocksize = 1 << ea_inode->i_blkbits; | |
| 321 | + int bh_count = (size + blocksize - 1) >> ea_inode->i_blkbits; | |
| 322 | + int tail_size = (size % blocksize) ?: blocksize; | |
| 323 | + struct buffer_head *bhs_inline[8]; | |
| 324 | + struct buffer_head **bhs = bhs_inline; | |
| 325 | + int i, ret; | |
| 326 | + | |
| 327 | + if (bh_count > ARRAY_SIZE(bhs_inline)) { | |
| 328 | + bhs = kmalloc_array(bh_count, sizeof(*bhs), GFP_NOFS); | |
| 329 | + if (!bhs) | |
| 330 | + return -ENOMEM; | |
| 331 | + } | |
| 333 | 332 | |
| 334 | − memcpy(copy_pos, bh->b_data, csize); | |
| 335 | − brelse(bh); | |
| 333 | + ret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count, | |
| 334 | + true /* wait */, bhs); | |
| 335 | + if (ret) | |
| 336 | + goto free_bhs; | |
| 336 | 337 | |
| 337 | − copy_pos += csize; | |
| 338 | − block += 1; | |
| 339 | − copied += csize; | |
| 338 | + for (i = 0; i < bh_count; i++) { | |
| 339 | + /* There shouldn't be any holes in ea_inode. */ | |
| 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 | 357 | static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, |