From patchwork Mon May 4 23:13:14 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Tomlinson X-Patchwork-Id: 6330731 Return-Path: X-Original-To: patchwork-linux-fsdevel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id BD1569F373 for ; Mon, 4 May 2015 23:19:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 966572022D for ; Mon, 4 May 2015 23:19:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5E6C720268 for ; Mon, 4 May 2015 23:19:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751015AbbEDXTp (ORCPT ); Mon, 4 May 2015 19:19:45 -0400 Received: from gate2.alliedtelesis.co.nz ([202.36.163.20]:56547 "EHLO gate2.alliedtelesis.co.nz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750973AbbEDXTn (ORCPT ); Mon, 4 May 2015 19:19:43 -0400 X-Greylist: delayed 378 seconds by postgrey-1.27 at vger.kernel.org; Mon, 04 May 2015 19:19:42 EDT Received: from mmarshal3.atlnz.lc (mmarshal3.atlnz.lc [10.32.18.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by gate2.alliedtelesis.co.nz (Postfix) with ESMTPS id 7310D80739 for ; Tue, 5 May 2015 11:13:25 +1200 (NZST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alliedtelesis.co.nz; s=mail; t=1430781205; bh=ABLCqgH+8Tf6d6Q58z1AMCKSZgrcm/OFm2abe/FOpIo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=VMtsJkq/R4XGfXFYM+xBCyutpe7hOiTCeGm5Le/TJL6hRJ3725HYs4EVWirhbPqFc 26FwcBw6nIDemSyQf/iDcf+aWNB/A2pyBkh8+p+gMDRkBBBAVRsDrTiFR2zHtgkFKS 1VBAdJCouiei5TAQ/3Lf/qY3G5TvDRGZcEsRiH38= Received: from smtp (Not Verified[10.32.16.33]) by mmarshal3.atlnz.lc with Trustwave SEG (v7, 3, 0, 7277) id ; Tue, 05 May 2015 11:13:21 +1200 Received: from markto-dl.ws.atlnz.lc (markto-dl.ws.atlnz.lc [10.33.24.11]) by smtp (Postfix) with ESMTP id 9CC1C13EEB1; Tue, 5 May 2015 11:13:20 +1200 (NZST) Received: by markto-dl.ws.atlnz.lc (Postfix, from userid 1155) id 96C1E30D661; Tue, 5 May 2015 11:13:21 +1200 (NZST) From: Mark Tomlinson To: linux-fsdevel@vger.kernel.org Cc: Mark Tomlinson Subject: [PATCH 1/1] Improve speed of JFFS2 at startup Date: Tue, 5 May 2015 11:13:14 +1200 Message-Id: <1430781194-5690-2-git-send-email-mark.tomlinson@alliedtelesis.co.nz> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1430781194-5690-1-git-send-email-mark.tomlinson@alliedtelesis.co.nz> References: <1430781194-5690-1-git-send-email-mark.tomlinson@alliedtelesis.co.nz> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP At startup, reading a directory (for example to do an ls), requires finding information on every file. To support JFFS2 recovery from partially written blocks, the JFFS2 driver must scan all data blocks to verify the checksums are correct just to be able to correctly report the length of a file. This will take some time, and will be dependent on the amount of data on the filesystem. What makes this worse is that any path lookup will lock the dentry cache to add the new entry. The JFFS2 driver then spends time finding the file information (reading the entire file), before it returns the new dentry information allowing the cache to be unlocked. During this time, no other files in the same directory can be opened or even tested for existence. However, there is no need for the dentry cache to be locked for the scan of the file. The JFFS2 driver already locks the file, so the file will not be deleted or modified. It also ensures that if another process tries to scan the same file, the second process will be blocked and the scan only proceed once. To make the scan occur without locking the cache, a new vfs call has been added which allows a filesystem to scan the file, but not return anything. When the lookup occurs after this, the JFFS2 driver will find this information and can quickly return the filled-in dentry. --- fs/jffs2/dir.c | 41 ++++++++++++++++++++++++++++++++++------- fs/namei.c | 44 ++++++++++++++++++++++++++++++++++++++------ include/linux/fs.h | 1 + 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 1ba5c97..69c0ec4 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -36,6 +36,7 @@ static int jffs2_rmdir (struct inode *,struct dentry *); static int jffs2_mknod (struct inode *,struct dentry *,umode_t,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); +static void jffs2_prescan(struct inode *dir_i, struct qstr *d_name); const struct file_operations jffs2_dir_operations = { @@ -51,6 +52,7 @@ const struct inode_operations jffs2_dir_inode_operations = { .create = jffs2_create, .lookup = jffs2_lookup, + .prescan = jffs2_prescan, .link = jffs2_link, .unlink = jffs2_unlink, .symlink = jffs2_symlink, @@ -74,8 +76,12 @@ const struct inode_operations jffs2_dir_inode_operations = and we use the same hash function as the dentries. Makes this nice and simple */ -static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, - unsigned int flags) +/* The prescan function does not have a dentry to fill in, so create this common function + * which is just passed the name and the inode for the directory. + * This function is very similar to the original jffs2_lookup, except for the arguments + * and the fact that the dentry (now not passed) is not updated. + */ +static struct inode *jffs2_lookup_common(struct inode *dir_i, struct qstr *d_name) { struct jffs2_inode_info *dir_f; struct jffs2_full_dirent *fd = NULL, *fd_list; @@ -84,7 +90,7 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, jffs2_dbg(1, "jffs2_lookup()\n"); - if (target->d_name.len > JFFS2_MAX_NAME_LEN) + if (d_name->len > JFFS2_MAX_NAME_LEN) return ERR_PTR(-ENAMETOOLONG); dir_f = JFFS2_INODE_INFO(dir_i); @@ -92,11 +98,11 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, mutex_lock(&dir_f->sem); /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */ - for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) { - if (fd_list->nhash == target->d_name.hash && + for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= d_name->hash; fd_list = fd_list->next) { + if (fd_list->nhash == d_name->hash && (!fd || fd_list->version > fd->version) && - strlen(fd_list->name) == target->d_name.len && - !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) { + strlen(fd_list->name) == d_name->len && + !strncmp(fd_list->name, d_name->name, d_name->len)) { fd = fd_list; } } @@ -108,6 +114,27 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, if (IS_ERR(inode)) pr_warn("iget() failed for ino #%u\n", ino); } + return inode; +} + +/* Fill in an inode, and store the information in cache. This allows a + * subsequent jffs2_lookup() call to proceed quickly, which is useful + * since the jffs2_lookup() call will have the directory entry cache + * locked. + */ +static void jffs2_prescan(struct inode *dir_i, struct qstr *d_name) +{ + (void)jffs2_lookup_common(dir_i, d_name); +} + +/* jffs2_lookup function has the same functionality as before, + * just refactored */ +static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, + unsigned int flags) +{ + struct inode *inode; + + inode = jffs2_lookup_common(dir_i, &target->d_name); return d_splice_alias(inode, target); } diff --git a/fs/namei.c b/fs/namei.c index 4a8d998b..9c6c293e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1322,13 +1322,12 @@ static void follow_dotdot(struct nameidata *nd) * * dir->d_inode->i_mutex must be held */ -static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir, - unsigned int flags, bool *need_lookup) +static struct dentry *lookup_dcache_no_alloc(struct qstr *name, struct dentry *dir, + unsigned int flags) { struct dentry *dentry; int error; - *need_lookup = false; dentry = d_lookup(dir, name); if (dentry) { if (dentry->d_flags & DCACHE_OP_REVALIDATE) { @@ -1345,6 +1344,16 @@ static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir, } } } + return dentry; +} + +static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir, + unsigned int flags, bool *need_lookup) +{ + struct dentry *dentry; + + *need_lookup = false; + dentry = lookup_dcache_no_alloc(name, dir, flags); if (!dentry) { dentry = d_alloc(dir, name); @@ -1497,9 +1506,32 @@ static int lookup_slow(struct nameidata *nd, struct path *path) parent = nd->path.dentry; BUG_ON(nd->inode != parent->d_inode); - mutex_lock(&parent->d_inode->i_mutex); - dentry = __lookup_hash(&nd->last, parent, nd->flags); - mutex_unlock(&parent->d_inode->i_mutex); + dentry = NULL; + if (parent->d_inode->i_op->prescan) { + /* First, just try to find the dentry in the cache. + * If it is not present, don't do anything. + */ + mutex_lock(&parent->d_inode->i_mutex); + dentry = lookup_dcache_no_alloc(&nd->last, parent, nd->flags); + mutex_unlock(&parent->d_inode->i_mutex); + + /* Not in cache. Warn filesystem layer that a lookup is coming. + * This can be done without the directory's mutex held, since + * no dentry is being filled in here. + */ + if (dentry == NULL) { + parent->d_inode->i_op->prescan(parent->d_inode, &nd->last); + } + } + if (dentry == NULL) { + /* Actually perform the lookup via the filesystem. The prescan + * done above will hopefully ensure this is now a quick operation, + * so the directory's mutex is not held for a long time. + */ + mutex_lock(&parent->d_inode->i_mutex); + dentry = __lookup_hash(&nd->last, parent, nd->flags); + mutex_unlock(&parent->d_inode->i_mutex); + } if (IS_ERR(dentry)) return PTR_ERR(dentry); path->mnt = nd->path.mnt; diff --git a/include/linux/fs.h b/include/linux/fs.h index 35ec87e..8d68867 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1639,6 +1639,7 @@ struct inode_operations { umode_t create_mode, int *opened); int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*set_acl)(struct inode *, struct posix_acl *, int); + void (*prescan) (struct inode *dir, struct qstr *name); /* WARNING: probably going away soon, do not use! */ int (*dentry_open)(struct dentry *, struct file *, const struct cred *);