From patchwork Mon Jul 17 06:20:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tahsin Erdogan X-Patchwork-Id: 789236 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3x9tSf6Glwz9sRg for ; Mon, 17 Jul 2017 16:20:18 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="lltE4/gx"; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751289AbdGQGUS (ORCPT ); Mon, 17 Jul 2017 02:20:18 -0400 Received: from mail-pf0-f176.google.com ([209.85.192.176]:36682 "EHLO mail-pf0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751253AbdGQGUQ (ORCPT ); Mon, 17 Jul 2017 02:20:16 -0400 Received: by mail-pf0-f176.google.com with SMTP id q86so71521476pfl.3 for ; Sun, 16 Jul 2017 23:20:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=y7HIpmdet/og6r6brUaNbuaaw8jqi9iDKTPx9VGBqFc=; b=lltE4/gxYCMqyBHXVCGCnc27+QAHEqcTIq39YQeH0quQIi5BG2fpmCGD8BBsTGOZGn CSZEQqXKov//J5FGk2DbrvNYsCvqy3dTvUJpm9W6upc/v2UXpFRZAT047q8lDjs1kQRo thXqgJzx6aA9odInk5XHKb8mUGPGiLaSXyqmodaefgTVCuWlV675c/x5kSr/gnB585nD K/hLF5/cnMJIGH3N7V3bLm00MIOcIgtKnicC/guAhtzS2mYx0O92mllOi2Z29gqksRCC 3Oy81hovc/bNyOdnFQdPCRNriyPo9aeiGxx2dZLEy0PnVinG0xGXpwVEMhpX8Ou4ze9t KMxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=y7HIpmdet/og6r6brUaNbuaaw8jqi9iDKTPx9VGBqFc=; b=i1FwVB7HcjeoZalU8cSbmhouivGwAcOa3HVo4bLCXSjZYB0OrBmV3JkQghtkC9Ue9M gLgQHb1GGxA8hMDJ73rV1KxXUHZZPUf4bUHL8hWQRqXDIAOIhZAPReT0oLweZwH4tr6q 9ID+JYMBZthXsjUkF4nZaqMqu+7aBWt/Z6ubxTDGRmo15RMXdh8ER2pSEJcOybkIS8qU SM1n3WaKOHG8EU5IUtVnBk5Fyb7XLOteS4rme/HuQ71ZTCIw+XF7AnLP6aIR+UWQrRZi kKV3nh79TS0ewf9QtqarFm7pSzxNSjiQ6gsNb4S1FtSoLf6EC+n7byfj6eiLkR4eZ7ol MAXw== X-Gm-Message-State: AIVw111VCiy/c91S1JgUwMfUAJEWDGEU+VKXmqyHyUvrvPdxQvjAgMvZ 5S9lpLVHeTHfgRee9l4TBw== X-Received: by 10.98.20.21 with SMTP id 21mr17391635pfu.29.1500272416003; Sun, 16 Jul 2017 23:20:16 -0700 (PDT) Received: from tahsin1.svl.corp.google.com ([100.123.230.167]) by smtp.gmail.com with ESMTPSA id 191sm30726459pfb.70.2017.07.16.23.20.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 16 Jul 2017 23:20:15 -0700 (PDT) From: Tahsin Erdogan To: Theodore Ts'o , Andreas Dilger , linux-ext4@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Tahsin Erdogan Subject: [PATCH v2] ext4: make xattr inode reads faster Date: Sun, 16 Jul 2017 23:20:12 -0700 Message-Id: <20170717062012.26331-1-tahsin@google.com> X-Mailer: git-send-email 2.13.2.932.g7449e964c-goog In-Reply-To: <705A22C6-4583-47D4-8FB0-B2820F7051F3@dilger.ca> References: <705A22C6-4583-47D4-8FB0-B2820F7051F3@dilger.ca> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org 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 --- v2: - updated ext4_find_entry() to also use ext4_bread_batch() - added wait parameter to ext4_bread_batch() fs/ext4/ext4.h | 2 ++ fs/ext4/inode.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/namei.c | 43 ++++++++++++++----------------------------- 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 9ebde0cd632e..e9440ed605c0 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2462,6 +2462,8 @@ extern void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid); int ext4_inode_is_fast_symlink(struct inode *inode); struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); +int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, + bool wait, struct buffer_head **bhs); int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); int ext4_get_block(struct inode *inode, sector_t iblock, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 3c600f02673f..70699940e20d 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, return ERR_PTR(-EIO); } +/* Read a contiguous batch of blocks. */ +int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, + bool wait, struct buffer_head **bhs) +{ + int i, err; + + for (i = 0; i < bh_count; i++) { + bhs[i] = ext4_getblk(NULL, inode, block + i, 0 /* map_flags */); + if (IS_ERR(bhs[i])) { + err = PTR_ERR(bhs[i]); + bh_count = i; + goto out_brelse; + } + } + + for (i = 0; i < bh_count; i++) + /* Note that NULL bhs[i] is valid because of holes. */ + if (bhs[i] && !buffer_uptodate(bhs[i])) + ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, + &bhs[i]); + + if (!wait) + return 0; + + for (i = 0; i < bh_count; i++) + if (bhs[i]) + wait_on_buffer(bhs[i]); + + for (i = 0; i < bh_count; i++) { + if (bhs[i] && !buffer_uptodate(bhs[i])) { + err = -EIO; + goto out_brelse; + } + } + return 0; + +out_brelse: + for (i = 0; i < bh_count; i++) { + brelse(bhs[i]); + bhs[i] = NULL; + } + return err; +} + int ext4_walk_page_buffers(handle_t *handle, struct buffer_head *head, unsigned from, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 13f0cadb1238..2758ff1b940f 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, struct super_block *sb; struct buffer_head *bh_use[NAMEI_RA_SIZE]; struct buffer_head *bh, *ret = NULL; - ext4_lblk_t start, block, b; + ext4_lblk_t start, block; const u8 *name = d_name->name; - int ra_max = 0; /* Number of bh's in the readahead + size_t ra_max = 0; /* Number of bh's in the readahead buffer, bh_use[] */ - int ra_ptr = 0; /* Current index into readahead + size_t ra_ptr = 0; /* Current index into readahead buffer */ - int num = 0; ext4_lblk_t nblocks; int i, namelen, retval; struct ext4_filename fname; @@ -1411,31 +1410,17 @@ static struct buffer_head * ext4_find_entry (struct inode *dir, if (ra_ptr >= ra_max) { /* Refill the readahead buffer */ ra_ptr = 0; - b = block; - for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) { - /* - * Terminate if we reach the end of the - * directory and must wrap, or if our - * search has finished at this block. - */ - if (b >= nblocks || (num && block == start)) { - bh_use[ra_max] = NULL; - break; - } - num++; - bh = ext4_getblk(NULL, dir, b++, 0); - if (IS_ERR(bh)) { - if (ra_max == 0) { - ret = bh; - goto cleanup_and_exit; - } - break; - } - bh_use[ra_max] = bh; - if (bh) - ll_rw_block(REQ_OP_READ, - REQ_META | REQ_PRIO, - 1, &bh); + if (block < start) + ra_max = start - block; + else + ra_max = nblocks - block; + ra_max = min(ra_max, ARRAY_SIZE(bh_use)); + retval = ext4_bread_batch(dir, block, ra_max, + false /* wait */, bh_use); + if (retval) { + ret = ERR_PTR(retval); + ra_max = 0; + goto cleanup_and_exit; } } if ((bh = bh_use[ra_ptr++]) == NULL) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index cff4f41ced61..68b5b4f9fbcb 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) */ static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size) { - unsigned long block = 0; - struct buffer_head *bh; - int blocksize = ea_inode->i_sb->s_blocksize; - size_t csize, copied = 0; - void *copy_pos = buf; - - while (copied < size) { - csize = (size - copied) > blocksize ? blocksize : size - copied; - bh = ext4_bread(NULL, ea_inode, block, 0); - if (IS_ERR(bh)) - return PTR_ERR(bh); - if (!bh) - return -EFSCORRUPTED; + int blocksize = 1 << ea_inode->i_blkbits; + int bh_count = (size + blocksize - 1) >> ea_inode->i_blkbits; + int tail_size = (size % blocksize) ?: blocksize; + struct buffer_head *bhs_inline[8]; + struct buffer_head **bhs = bhs_inline; + int i, ret; + + if (bh_count > ARRAY_SIZE(bhs_inline)) { + bhs = kmalloc_array(bh_count, sizeof(*bhs), GFP_NOFS); + if (!bhs) + return -ENOMEM; + } - memcpy(copy_pos, bh->b_data, csize); - brelse(bh); + ret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count, + true /* wait */, bhs); + if (ret) + goto free_bhs; - copy_pos += csize; - block += 1; - copied += csize; + for (i = 0; i < bh_count; i++) { + /* There shouldn't be any holes in ea_inode. */ + if (!bhs[i]) { + ret = -EFSCORRUPTED; + goto put_bhs; + } + memcpy((char *)buf + blocksize * i, bhs[i]->b_data, + i < bh_count - 1 ? blocksize : tail_size); } - return 0; + ret = 0; +put_bhs: + for (i = 0; i < bh_count; i++) + brelse(bhs[i]); +free_bhs: + if (bhs != bhs_inline) + kfree(bhs); + return ret; } static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,