Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp469480imu; Tue, 22 Jan 2019 23:21:20 -0800 (PST) X-Google-Smtp-Source: ALg8bN6e4UxcjkAQe0qrbmncWCS8fJE+5Bls8UNY/6vCbVS/Mzs7PtKUzkKROTH941xh3u7UaqmJ X-Received: by 2002:a17:902:ba89:: with SMTP id k9mr1162590pls.189.1548228079999; Tue, 22 Jan 2019 23:21:19 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548228079; cv=none; d=google.com; s=arc-20160816; b=uezrw4NW6f0Q0ClZN58gD/quiGGN7iWPRs+V6MAEKZ2ilup6HY81n00jkhFEGvpchu W5LuQkgTmgJe7lUvjbQiplx1fGmntsnDd116hNWVBgdS2k0pO8KP4MXj/Pi3DlDxUpAS 7Ne9uAsNVchfkhxBCUsFyH5hz07Unphn6VOYzYkVxmWMz4E1w30eexuCSXYobyM7JLdE XaTU45Hqt4JOJC0eG6J1p9hbaP5oDjLtAyhKfE1AowAPFde6S5T5x7AsLLCqRuTPHyzt EvXfd5OcmqcB2QtZqzNrSbpdXpaHBYJPjG+zDT9gA9A2y49M7KYWD3uYxCtrck1VmEsc 7Nqg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:message-id:date:subject:cc :to:from; bh=NCWg+9eXwQTamUIWLHy/Fbnl+YE9Ya5R3pE1JvZ3KX0=; b=MlN7TCvgvd5Uvfl8NPJQecU5/8Te9kj4XCVXNK42gjJJktev/1GtzCAJkOpYswxtuK iFqKVlm6CNzXH4ART1U9+jxiQqh4rPUoZhlEl0Ao1pPDY3EZRq1Gahn7RyG3mlviOFnF YWFcXCJANQugkvtTlxxqo9UmjiLrntdA6zUJgZ95DHDr1YI09UTMo5Jih/Wpg9VsiO/v hanb4Ea2ncvRHvrXJ3+WzOkyVg2AmI6QjbztFBzofbMuMbskJW4XxK+uSu20LTARO4n8 Y/g0zn0eRUTctZxmuGVm1xos6f0jsUPRvNmWw7AJO3Xy5LCXdpeZCk+Udn47OpNyUWJZ Lofg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id h17si18371377pgd.538.2019.01.22.23.21.04; Tue, 22 Jan 2019 23:21:19 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726253AbfAWHTU (ORCPT + 99 others); Wed, 23 Jan 2019 02:19:20 -0500 Received: from szxga07-in.huawei.com ([45.249.212.35]:41894 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1725973AbfAWHTU (ORCPT ); Wed, 23 Jan 2019 02:19:20 -0500 Received: from DGGEMS411-HUB.china.huawei.com (unknown [172.30.72.58]) by Forcepoint Email with ESMTP id E471136CEFC1D88072A9; Wed, 23 Jan 2019 15:19:17 +0800 (CST) Received: from huawei.com (10.90.53.225) by DGGEMS411-HUB.china.huawei.com (10.3.19.211) with Microsoft SMTP Server id 14.3.408.0; Wed, 23 Jan 2019 15:19:12 +0800 From: Yufen Yu To: , , CC: , , , Subject: [PATCH v2] jffs2: safely remove obsolete dirent from the f->dents list Date: Wed, 23 Jan 2019 15:22:49 +0800 Message-ID: <20190123072249.103847-1-yuyufen@huawei.com> X-Mailer: git-send-email 2.16.2.dirty MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.90.53.225] X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org We may delete a bunch of directory entries, operating such as: getdents(), unlink(), getdents()..., until the end of the directory. jffs2 handles f_pos on the directory merely as the position on the f->dents list. So, the next getdents() may skip some entries before f_pos, if we remove some entries from the list between two getdents(), resulting in some entries of the directory cannot be deleted. Commit 15953580e79b is introduced to resolve this bug by not removing delete entries from the list immediately. However, when CONFIG_JFFS2_SUMMARY is not set, it can cause the following issues: * 'deletion' dirents is always in the f->dents list, wasting memory resource. For example: There is a file named 'file1'. Then we rename it: mv file1 file2; mv file2 file3; ... mv file99999 file1000000 All of file1~file1000000 dirents always in the f->dents list. * Though, the list we're traversing is already ordered by CRC, it could waste much CPU time when the list is very long. To fix, we define two variables in struct jffs2_inode_info: nr_dir_opening, obsolete_count, and two new functions: jffs2_dir_open(), jffs2_dir_release(). When open a directory, jffs2_dir_open() will increase nr_dir_opening, which will be decreased by jffs2_dir_release(). if the value is 0, it means nobody open the dir and nobody in getdents()/seek() semantics. Thus, we can remove all obsolete dirent from the list. When delete a file, jffs2_do_unlink() can remove the dirent directly if nobody open the directory(i.e. nr_dir_opending == 0). Otherwise, it just increase obsolete_count, denoting obsolete dirent count of the list. Fixes: 15953580e79b ("[JFFS2] Improve getdents vs. f_pos handling on NOR flash.") Signed-off-by: Yufen Yu --- fs/jffs2/dir.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/jffs2_fs_i.h | 7 +++++++ fs/jffs2/super.c | 4 ++++ fs/jffs2/write.c | 30 +++++++++++++++++++--------- include/uapi/linux/jffs2.h | 4 ++++ 5 files changed, 85 insertions(+), 9 deletions(-) diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index f20cff1194bb..aed872dcd0ca 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -37,6 +37,8 @@ static int jffs2_mknod (struct inode *,struct dentry *,umode_t,dev_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); +static int jffs2_dir_release (struct inode *, struct file *); +static int jffs2_dir_open (struct inode *, struct file *); const struct file_operations jffs2_dir_operations = { @@ -45,6 +47,8 @@ const struct file_operations jffs2_dir_operations = .unlocked_ioctl=jffs2_ioctl, .fsync = jffs2_fsync, .llseek = generic_file_llseek, + .open = jffs2_dir_open, + .release = jffs2_dir_release, }; @@ -865,3 +869,48 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, return 0; } +static int jffs2_dir_open(struct inode *dir_i, struct file *filp) +{ +#ifndef CONFIG_JFFS2_SUMMARY + struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); + atomic_inc(&dir_f->nr_dir_opening); +#endif + + return 0; +} + +static int jffs2_dir_release(struct inode *dir_i, struct file *filp) +{ +#ifndef CONFIG_JFFS2_SUMMARY + struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); + + BUG_ON(atomic_read(&dir_f->nr_dir_opening) <= 0); + + mutex_lock(&dir_f->sem); + /* jffs2_dir_open may increase nr_dir_opening after + * atomic_dec_and_test() returning true. + * However, it cannot traverse the list until hold + * mutex dir_f->sem lock, so that we can go on + * removing.*/ + if (atomic_dec_and_test(&dir_f->nr_dir_opening) && + dir_f->obsolete_count > JFFS2_OBS_DIRENT_LIMIT) { + struct jffs2_full_dirent **prev = &dir_f->dents; + + /* remove all obsolete dirent from the list, which + * can save memory space and reduce CPU time for + * traverse the list */ + while(*prev) { + if ((*prev)->raw == NULL && (*prev)->ino == 0) { + struct jffs2_full_dirent *this = *prev; + *prev = this->next; + jffs2_free_full_dirent(this); + } else + prev = &((*prev)->next); + } + dir_f->obsolete_count = 0; + } + mutex_unlock(&dir_f->sem); +#endif + + return 0; +} diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index 2e4a86763c07..a4da25d16cb4 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -50,6 +50,13 @@ struct jffs2_inode_info { uint16_t flags; uint8_t usercompr; + + /* obsolete dirent count in the list of 'dents' */ + uint8_t obsolete_count; + + /* Directory open refcount */ + atomic_t nr_dir_opening; + struct inode vfs_inode; }; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 87bdf0f4cba1..bf181b0ade9c 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -41,6 +41,10 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb) f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL); if (!f) return NULL; + + atomic_set(&f->nr_dir_opening, 0); + f->obsolete_count = 0; + return &f->vfs_inode; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index cda9a361368e..780b4fd9af51 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -600,29 +600,41 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, } else { uint32_t nhash = full_name_hash(NULL, name, namelen); - fd = dir_f->dents; + struct jffs2_full_dirent **prev = &dir_f->dents; + /* We don't actually want to reserve any space, but we do want to be holding the alloc_sem when we write to flash */ mutex_lock(&c->alloc_sem); mutex_lock(&dir_f->sem); - for (fd = dir_f->dents; fd; fd = fd->next) { - if (fd->nhash == nhash && - !memcmp(fd->name, name, namelen) && - !fd->name[namelen]) { + while((*prev) && (*prev)->nhash <= nhash) { + if ((*prev)->nhash == nhash && + !memcmp((*prev)->name, name, namelen) && + !(*prev)->name[namelen]) { + fd = *prev; jffs2_dbg(1, "Marking old dirent node (ino #%u) @%08x obsolete\n", fd->ino, ref_offset(fd->raw)); jffs2_mark_node_obsolete(c, fd->raw); - /* We don't want to remove it from the list immediately, - because that screws up getdents()/seek() semantics even - more than they're screwed already. Turn it into a - node-less deletion dirent instead -- a placeholder */ fd->raw = NULL; fd->ino = 0; + + /* if nr_dir_openning is 0, we think nobody open the dir, and + * nobody being in getdents()/seek() semantics. Thus, we can + * safely remove this obsolete dirent from the list. Otherwise, + * we just increase obsolete_count, and finally delete it in + * jffs2_dir_release() */ + if (atomic_read(&dir_f->nr_dir_opening) == 0) { + *prev = fd->next; + jffs2_free_full_dirent(fd); + } else + dir_f->obsolete_count++; + break; } + prev = &((*prev)->next); } + mutex_unlock(&dir_f->sem); } diff --git a/include/uapi/linux/jffs2.h b/include/uapi/linux/jffs2.h index a18b719f49d4..dff3ac2d6b0c 100644 --- a/include/uapi/linux/jffs2.h +++ b/include/uapi/linux/jffs2.h @@ -35,6 +35,10 @@ */ #define JFFS2_MAX_NAME_LEN 254 +/* The obsolete dirent of dents list limit. When the number over + * this limit, we can remove the obsoleted dents. */ +#define JFFS2_OBS_DIRENT_LIMIT 64 + /* How small can we sensibly write nodes? */ #define JFFS2_MIN_DATA_LEN 128 -- 2.14.4