From nobody Sat Sep 26 10:02:10 2026 Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8B7694A64D2 for ; Wed, 2 Sep 2026 15:25:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362744; cv=none; b=Kt9h1HCTxfff14PMiFsu18c/GF4wo1WwYhSotnDA6HX4/LcXMWOWCdinqnCnFBoY3u+A5LKGUWxn8faFkvnl3omzbVEOlu9xbig3Rsiwhc/kdb39qkdeHuNGXgGSsFei04hpfbbsCBAvzC3P4u04eUOM/zzDzDEd8R5+Ks4ULoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362744; c=relaxed/simple; bh=vdnxUDzAMzKxj1Sl6XAuhDSq9GQuOcAfHPdObwwi9lg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=udvs+k6JHIGYO/AgmzeWUQiaoNEazwUE9P/7klUdu8p67vJUyj8PEHlCzaCWQQ8snVvMZ4QzFpbS6geBJWE7cgAU/xerkHb1e4+oBjdJMmotZ/f4EUvluBrUmXNgk9PKtWzY1I8rLHZgRGR3j8Q0MB6ADsKEEbrcbK2orW6hnqQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mxL2OQr9; arc=none smtp.client-ip=209.85.215.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mxL2OQr9" Received: by mail-pg1-f172.google.com with SMTP id 41be03b00d2f7-cbb8b54fcf8so1456546a12.0 for ; Wed, 02 Sep 2026 08:25:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788362742; x=1788967542; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=GKUQ99wuylFJvuvslMRQYoPvh++T8GD2C+XNyd/ppOc=; b=mxL2OQr9Yf57kqpyNZaO+SowGD8ud43AtEwAEWddP7Xo9YHHUu+HwfIdxpkmj7n5iJ kXTA5708YKvL9+HwGymFnyJ6Hgc8YMx/o4s+5dx8jhNtw8avnWhj+QXAPLZn7dPLx76y SXBJ0RaieuJBS5R0i51AZWbI3oig+0kbr8G/Uc7xo9NUZmPZeToo3tlibQeixBI3KP9X nTv6ZpBa0vfDPzuPZbKwfcGCoGk2PAk7CRwuLrvEMx+1wu0Olz5dO3j+hZFdleKujmHm McOtpZLvPERH7IKIiWuhHZy1OC0NIVigGMmcL1b9OwEXUzZMiMxYeT/9aaR7wIfMIr3J 43Mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788362742; x=1788967542; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=GKUQ99wuylFJvuvslMRQYoPvh++T8GD2C+XNyd/ppOc=; b=g3mU2n9o2mrIB2lvRzXKarSMMx/FxxabsAXR3eVA+mZfBlM1+ztlHJGXXa3reAqha9 Jyk/7BTB+hDeyunA1EQeuXjYkVNZ3QbKt7UJFoVO34cLi38xVonaXrB0dnigM5bqbDq4 QT0b4H0Gds0oWlRBYtG4vWD7lgvdw+YPvoG+2T9Gr8np4IKjrLhznpSuj4uW+XRKsOfK 8qx9stucd7t8vgXBLoc6QQBnS6lX3wPrQtpqrNpNadHl9VG308DCkZaJxcF9TMhvAfll W+QZvRr3zwjv/xuk1fWrC1IwuEQ9z+O3vEfoPZ9srJmDxRr612g+yUS1+VO3/5jaiUYs 3CTQ== X-Forwarded-Encrypted: i=1; AKwUvBzfY/n+fnmqO16KyIkK1/zH/Mi/6jCAAhMMrznNOKLx3xmUWQQS+VlyZGMDm+sFIJIPeruDeRn7AFdFGxk=@vger.kernel.org X-Gm-Message-State: AFuF++lGO1FH/w3125FjqL4lHBtR2VEOlUQNdzL8giuSUagtSKY2n7Fj X0mvSbVGcF2SPBJUt4H5uIpPYGGXJtBI5xMyS9UL1MTi698b1hy5B405 X-Gm-Gg: AYBFou2HdtPoHtbgGgBPIOFNSzn1d4OoMmX8YcU6/18JmspuMNAjcSeWxNY8yvwg75J iuUxWdip71kl7UvoRnm381HTV4NN2vGgbGjQHevjRg6+jTqBWlSWSQiOz1qmKE1KBAFfLl04hVH u56pf3CAY9RzWHqcxlLMjMuCTZHSTg0wLOhS9C71JrYTDxFN8iKXgAaGCD88tJ6KAB0EpCV++XJ Jq6Mz7OeZ0NAvAuClKKlgLJQtrZfIO37xAmOY4uspjxJqMBUxVgHWXzxOm8uGM5p4akpP8KEOiZ 1gs5mcT0LC9uwuuQnLc4s5n08cVnEsf38NzI8I3IzcAeIM62/VJ1S/XCkDWJrXT9tu4KeeqxGWH Y5nWx7Eh7VUJmMBt3XjeHjY3QkuxiS24B3tO+M90jnjxOvndEuO82mJayCsbBMS+xtzYDP/BDIw AYdUXuzBhOva9IUliChPqPwrQgguGDy4VvGABmuKcAfZhb8Tiwx6cJpLVSU7qxM9FmXgO4U+Imq g== X-Received: by 2002:a17:90b:2604:b0:38e:c7b0:84ad with SMTP id 98e67ed59e1d1-39aedce056cmr8573120a91.0.1788362739146; Wed, 02 Sep 2026 08:25:39 -0700 (PDT) Received: from localhost.localdomain ([47.100.192.162]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990bd21e9dsm12359242a91.4.2026.09.02.08.25.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 08:25:37 -0700 (PDT) From: Yang Wen To: linkinjeon@kernel.org, sj1557.seo@samsung.com, chizhiling@163.com Cc: yuezhang.mo@sony.com, exfat@lists.linux.dev, linux-kernel@vger.kernel.org, Yang Wen Subject: [PATCH v3 1/3] exfat: add a Bloom filter for negative name lookups Date: Wed, 2 Sep 2026 23:24:53 +0800 Message-Id: <20260902152455.1273557-2-anmuxixixi@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260902152455.1273557-1-anmuxixixi@gmail.com> References: <20260902152455.1273557-1-anmuxixixi@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Negative name lookups scan a directory from the beginning. Repeating this scan before creating each file makes bulk creation approach O(N^2). Add a 64 KiB per-directory Bloom filter. Build it lazily after a directory reaches 1024 on-disk entries. A definite miss skips the directory scan, while a possible match follows the normal lookup path so hash collisions cannot affect correctness. Signed-off-by: Yang Wen Reviewed-by: Chi Zhiling --- fs/exfat/dir.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ fs/exfat/exfat_fs.h | 13 ++++ fs/exfat/inode.c | 1 + fs/exfat/namei.c | 6 +- fs/exfat/super.c | 1 + 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index fe73b1380c5d..5145f09bcee6 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include =20 #include "exfat_raw.h" #include "exfat_fs.h" @@ -65,6 +67,140 @@ static int exfat_get_uniname_from_ext_entry(struct supe= r_block *sb, return 0; } =20 +static u32 exfat_name_filter_hash(struct super_block *sb, + const struct exfat_uni_name *name) +{ + unsigned long hash =3D init_name_hash(NULL); + int i; + + for (i =3D 0; i < name->name_len; i++) + hash =3D partial_name_hash(exfat_toupper(sb, name->name[i]), hash); + + return end_name_hash(hash); +} + +static void exfat_name_filter_indexes(struct super_block *sb, + const struct exfat_uni_name *name, + unsigned int indexes[3]) +{ + u32 hash =3D exfat_name_filter_hash(sb, name); + + indexes[0] =3D hash_32(hash, EXFAT_NAME_FILTER_ORDER); + indexes[1] =3D hash_32(hash ^ 0x9e3779b9U, EXFAT_NAME_FILTER_ORDER); + indexes[2] =3D hash_32(rol32(hash, 16) ^ 0x85ebca6bU, + EXFAT_NAME_FILTER_ORDER); +} + +void exfat_name_filter_free(struct inode *inode) +{ + struct exfat_inode_info *ei =3D EXFAT_I(inode); + + kvfree(ei->name_filter); + ei->name_filter =3D NULL; +} + +bool exfat_name_filter_maybe_contains(struct inode *inode, + const struct exfat_uni_name *name) +{ + struct exfat_inode_info *ei =3D EXFAT_I(inode); + unsigned int indexes[3]; + + if (!ei->name_filter) + return true; + + exfat_name_filter_indexes(inode->i_sb, name, indexes); + return test_bit(indexes[0], ei->name_filter) && + test_bit(indexes[1], ei->name_filter) && + test_bit(indexes[2], ei->name_filter); +} + +void exfat_name_filter_add(struct inode *inode, + const struct exfat_uni_name *name) +{ + struct exfat_inode_info *ei =3D EXFAT_I(inode); + unsigned int indexes[3]; + + if (!ei->name_filter) + return; + + exfat_name_filter_indexes(inode->i_sb, name, indexes); + __set_bit(indexes[0], ei->name_filter); + __set_bit(indexes[1], ei->name_filter); + __set_bit(indexes[2], ei->name_filter); +} + +/* + * Build a complete filter only after a directory becomes large enough for + * repeated negative linear lookups to matter. A filter hit is never trust= ed: + * it only allows definite misses to skip the on-disk scan. + */ +static void exfat_build_name_filter(struct super_block *sb, + struct exfat_inode_info *ei, + struct exfat_chain *p_dir) +{ + unsigned long *filter; + struct exfat_chain clu; + unsigned int clu_count =3D 0; + struct inode *inode =3D &ei->vfs_inode; + struct exfat_sb_info *sbi =3D EXFAT_SB(sb); + int i; + + if (ei->name_filter || + exfat_bytes_to_dentries(i_size_read(inode)) < + EXFAT_NAME_FILTER_MIN_DENTRIES) + return; + + filter =3D kvzalloc(EXFAT_NAME_FILTER_BYTES, GFP_NOFS); + if (!filter) + return; + + exfat_chain_dup(&clu, p_dir); + while (clu.dir !=3D EXFAT_EOF_CLUSTER) { + for (i =3D 0; i < sbi->dentries_per_clu; i++) { + struct exfat_uni_name name =3D { }; + struct exfat_dentry *ep; + struct buffer_head *bh; + unsigned int type; + unsigned int indexes[3]; + int len; + + ep =3D exfat_get_dentry(sb, &clu, i, &bh); + if (!ep) + goto abort; + + type =3D exfat_get_entry_type(ep); + brelse(bh); + if (type =3D=3D TYPE_UNUSED) + goto complete; + if (type !=3D TYPE_FILE && type !=3D TYPE_DIR) + continue; + + if (exfat_get_uniname_from_ext_entry(sb, &clu, i, name.name)) + goto abort; + for (len =3D 0; len <=3D MAX_NAME_LENGTH && name.name[len]; len++) + ; + if (!len || len > MAX_NAME_LENGTH) + goto abort; + name.name_len =3D len; + exfat_name_filter_indexes(sb, &name, indexes); + __set_bit(indexes[0], filter); + __set_bit(indexes[1], filter); + __set_bit(indexes[2], filter); + } + + if (exfat_chain_advance(sb, &clu, 1)) + goto abort; + if (unlikely(++clu_count > EXFAT_DATA_CLUSTER_COUNT(sbi))) + goto abort; + } + +complete: + ei->name_filter =3D filter; + return; +abort: + kvfree(filter); +} + /* read a directory entry from the opened directory */ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_d= ir_entry *dir_entry) { @@ -992,6 +1128,8 @@ int exfat_find_dir_entry(struct super_block *sb, struc= t exfat_inode_info *ei, =20 if (num_entries < 0) return num_entries; + if (!exfat_name_filter_maybe_contains(&ei->vfs_inode, p_uniname)) + return -ENOENT; =20 dentries_per_clu =3D sbi->dentries_per_clu; =20 @@ -1153,6 +1291,8 @@ int exfat_find_dir_entry(struct super_block *sb, stru= ct exfat_inode_info *ei, ei->hint_femp.count =3D 0; } =20 + exfat_build_name_filter(sb, ei, p_dir); + /* initialized hint_stat */ hint_stat->clu =3D p_dir->dir; hint_stat->eidx =3D 0; diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index a9131fe03302..5187dee47cf1 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -120,6 +120,11 @@ enum { #define DIR_CACHE_SIZE \ (DIV_ROUND_UP(ES_MAX_ENTRY_NUM << DENTRY_SIZE_BITS, SECTOR_SIZE) + 1) =20 +#define EXFAT_NAME_FILTER_ORDER 19 +#define EXFAT_NAME_FILTER_BITS BIT(EXFAT_NAME_FILTER_ORDER) +#define EXFAT_NAME_FILTER_BYTES (EXFAT_NAME_FILTER_BITS >> 3) +#define EXFAT_NAME_FILTER_MIN_DENTRIES 1024 + /* Superblock flags */ #define EXFAT_FLAGS_SHUTDOWN 1 =20 @@ -284,6 +289,8 @@ struct exfat_inode_info { struct exfat_hint hint_stat; /* hint for first empty entry */ struct exfat_hint_femp hint_femp; + /* Complete, in-memory Bloom filter of directory names */ + unsigned long *name_filter; =20 spinlock_t cache_lru_lock; struct list_head cache_lru; @@ -619,6 +626,12 @@ int exfat_read_volume_label(struct super_block *sb, int exfat_write_volume_label(struct super_block *sb, struct exfat_uni_name *label); =20 +bool exfat_name_filter_maybe_contains(struct inode *inode, + const struct exfat_uni_name *name); +void exfat_name_filter_add(struct inode *inode, + const struct exfat_uni_name *name); +void exfat_name_filter_free(struct inode *inode); + static inline int exfat_chain_advance(struct super_block *sb, struct exfat_chain *chain, unsigned int step) { diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index ccd13630187e..0d0c6f817775 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -445,6 +445,7 @@ struct inode *exfat_build_inode(struct super_block *sb, void exfat_evict_inode(struct inode *inode) { truncate_inode_pages_final(&inode->i_data); + exfat_name_filter_free(inode); =20 if (!inode->i_nlink) { i_size_write(inode, 0); diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index a4dc83b5949c..96f33226f2c0 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -526,6 +526,7 @@ static int exfat_add_entry(struct inode *inode, const c= har *path, } =20 info->entry =3D dentry; + exfat_name_filter_add(inode, &uniname); info->flags =3D ALLOC_NO_FAT_CHAIN; info->type =3D type; =20 @@ -802,7 +803,6 @@ static int exfat_unlink(struct inode *dir, struct dentr= y *dentry) =20 /* update the directory entry */ exfat_remove_entries(inode, &es, ES_IDX_FILE, true); - err =3D exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (err) goto unlock; @@ -957,7 +957,6 @@ static int exfat_rmdir(struct inode *dir, struct dentry= *dentry) exfat_set_volume_dirty(sb); =20 exfat_remove_entries(inode, &es, ES_IDX_FILE, true); - err =3D exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); if (err) goto unlock; @@ -1215,6 +1214,8 @@ static int __exfat_rename(struct inode *old_parent_in= ode, ret =3D exfat_rename_file(new_parent_inode, &uni_name, ei); else ret =3D exfat_move_file(new_parent_inode, &uni_name, ei); + if (!ret) + exfat_name_filter_add(new_parent_inode, &uni_name); =20 if (!ret && new_inode) { struct exfat_entry_set_cache es; @@ -1227,7 +1228,6 @@ static int __exfat_rename(struct inode *old_parent_in= ode, } =20 exfat_remove_entries(new_inode, &es, ES_IDX_FILE, true); - ret =3D exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); if (ret) goto del_out; diff --git a/fs/exfat/super.c b/fs/exfat/super.c index a9ea36ba2693..f73cf2d02365 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -195,6 +195,7 @@ static struct inode *exfat_alloc_inode(struct super_blo= ck *sb) if (!ei) return NULL; =20 + ei->name_filter =3D NULL; return &ei->vfs_inode; } =20 --=20 2.34.1 From nobody Sat Sep 26 10:02:10 2026 Received: from mail-pj1-f49.google.com (mail-pj1-f49.google.com [209.85.216.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF7C74A5C57 for ; Wed, 2 Sep 2026 15:25:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362750; cv=none; b=MbAkx6dDMRT8g7QcX72pvopEO4SOhMUHcrVCi2b+/ma7NKMAM0bGslmNLtpykeskzGBun5kXp7h2inG/PPSedzwbUdVJbKH864ozda4Utl3I8m3NwJi4Kx7Ei6Yb1dFOq+a7w8XfvStnbWhWLsHZb5S0msKD6Q4SrC/Xeq+MCzA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362750; c=relaxed/simple; bh=eV++YHSYLH97mvqang/bv+b6/cf5SSX/9cfvxcjxKQg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jR01HbexFEVL7fIEpPJqiIhwXdjWjNEjY1Eo5Th0lfH+Ni0Khi92BPHAsgEJfdWi2dKDFQPWgsaJdFRKIBJbFxqbRMQRNJb/oPVSy23KBB6/CA9sGKRCQXZGGVygLoDvh9G76J1nxDUMpjGcuaROjhx0wbJMw/ZfDqkd2pX47Gc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DYoJ5BFP; arc=none smtp.client-ip=209.85.216.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DYoJ5BFP" Received: by mail-pj1-f49.google.com with SMTP id 98e67ed59e1d1-3856d6fbcb3so1215108a91.2 for ; Wed, 02 Sep 2026 08:25:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788362746; x=1788967546; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=dWFKIedgYv9yHKxOFWuvh3g0Aa0J3QdCQirkmfefCog=; b=DYoJ5BFPZNbLztujD7f8ssqYhdNEZUTI8eB9ixNOJZc92MQXOhkSgOc+sS30CnrNT1 iBdAqyjtvNsYDLN+xC/lf0beFWoFvq6OLW8ct9iBqB1HeIJfj99Hg0QdI9zBR/lhhkNU MomBt0jLWor3RSRlsYd74GVfn233WOQhHAHmTIMqXKKzt1ChpcVA4peOZ4cEpD5Bh5Jo nIG+SYTnIQL7wplM8nHBZh/AUwlfBEygZgtslLtm2WTrQCg7zWasDGilB6Ec13UpmSG4 4TnmGshX00mM8G+gRlfZUXoZNM1/3Ker/aTGpMtIJG3sLfXyHxrB/5sGLd3K51vwFxTP 6SZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788362746; x=1788967546; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=dWFKIedgYv9yHKxOFWuvh3g0Aa0J3QdCQirkmfefCog=; b=edXReWYgUzUT7ZTyQs8VWwleY54OLqpMei1LYfdgBjDktpKyOUSmHkIMOrSi0Q/pG2 7VEcLM9kX+4bkX9NNx+ujnwSmylDvR9FOrMkFYXIDxcAQM6uMYpqvMr4CyjQ/osT9mhj SGUtbnomNaj+8GXPjk8EKkFZwWiZnr8P5yYeFy32n6xniexHQRO4OK0JzMnnya1nZoMu uCmUbQKPDEAjGxpboBH6FaZC4VDAK+VqpXY1Gm4WuqvQ6iV7QM9jvvxgsyhr4HhGeC2F VAiDNUYHI8rTJQjm8pdcyUCNa+zCuLfN+7qYQm3RN3tnUNAdyh3edeig8kmxgN7i9Y9R /n0g== X-Forwarded-Encrypted: i=1; AKwUvBxkPZeOVwMwDO2ghyc+Zk2/ql2t03guSJ/JLFdPjNnHIooW6sR0ATxk+zwVxt4AAQxnQZuxTyxAphfXpyc=@vger.kernel.org X-Gm-Message-State: AFuF++nj52PtfHgJ9rCV8ZdRyLvu830P4lTdAJHSHJwNXdc46mwTX2LV cCReSw9CR8DJlZv0BbkWpbY6R+0TsmgSw9Fn0B+/9yPjabBlEGeXA1ES X-Gm-Gg: AYBFou0/wmdUi+DRt1S53UshQyOo62gm5WYoX+B8i1GhNFxnQSyHLlokKOvL91C7bk1 d/0r0Gbq0Xzc7T2GBOyt+WLF9Qxm9YQZSBLvMT0tFXGKMmzJtOy1WOi1HZqvRtt64opv7JRVpaV XoWdNqHY0C+nIAigcNrCfcBGyx/4mkPqdl3+C5C/fCgJxhEAExFDCcYjXcw9RkpR1rQqk3shCb6 dK0Juk+sc7qZoC62xICGXqRPvFgry3bLL415ngG4b8HW/n35sBJyRoMB3OgRYd8ZOWhHWn3MkmK YV+Grh3v5kvgVuR9bVTLQ+scyiQMCo0Ue//fo4whJCQlDTRVSVJJmpg5W3zWh9PoTPztp3Soepv dqp18ttpGwZykxUx8/qtMSEx3gR49O3IzmejefT6Isu0+OK9y/25sPMWCsrhXZOMVfsIH28dZA1 QOAAs/eYYf54xOFy6/gzS0bi/DjrOfOwwwqSEqa17OqRCRcSiOg6iKFzzholjT3hVqa1+4U2Ldl A== X-Received: by 2002:a17:90b:4d06:b0:398:bf7e:b267 with SMTP id 98e67ed59e1d1-39aedec7bd8mr7717304a91.2.1788362745721; Wed, 02 Sep 2026 08:25:45 -0700 (PDT) Received: from localhost.localdomain ([47.100.192.162]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990bd21e9dsm12359242a91.4.2026.09.02.08.25.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 08:25:45 -0700 (PDT) From: Yang Wen To: linkinjeon@kernel.org, sj1557.seo@samsung.com, chizhiling@163.com Cc: yuezhang.mo@sony.com, exfat@lists.linux.dev, linux-kernel@vger.kernel.org, Yang Wen Subject: [PATCH v3 2/3] exfat: retain the next empty directory entry hint Date: Wed, 2 Sep 2026 23:24:54 +0800 Message-Id: <20260902152455.1273557-3-anmuxixixi@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260902152455.1273557-1-anmuxixixi@gmail.com> References: <20260902152455.1273557-1-anmuxixixi@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" After a Bloom filter miss skips the name scan, finding an empty entry can still rescan the directory from the beginning. Advance the empty-entry hint past each newly allocated entry set and retain it while the name filter is active. Invalidate the hint whenever create rollback, unlink, rmdir, rename, or move can free entries, so the next allocation rediscovers earlier holes. This preserves the fast append path during bulk creation without allowing stale hints to cause unbounded directory growth. Signed-off-by: Yang Wen Reviewed-by: Chi Zhiling --- fs/exfat/namei.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index 96f33226f2c0..56772167ba5d 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -199,6 +199,50 @@ const struct dentry_operations exfat_utf8_dentry_ops = =3D { .d_compare =3D exfat_utf8_d_cmp, }; =20 +static void exfat_set_next_empty_hint(struct inode *inode, + struct exfat_chain *p_dir, int dentry, + int num_entries, + struct exfat_entry_set_cache *es) +{ + struct exfat_inode_info *ei =3D EXFAT_I(inode); + struct exfat_sb_info *sbi =3D EXFAT_SB(inode->i_sb); + unsigned int next =3D dentry + num_entries; + unsigned int total =3D exfat_cluster_to_dentries(sbi, p_dir->size); + struct exfat_chain cur; + + if (next >=3D total) { + exfat_chain_set(&ei->hint_femp.cur, EXFAT_EOF_CLUSTER, 0, + p_dir->flags); + ei->hint_femp.eidx =3D total; + ei->hint_femp.count =3D 0; + return; + } + + cur.dir =3D exfat_sector_to_cluster(sbi, + es->bh[es->num_bh - 1]->b_blocknr); + cur.flags =3D p_dir->flags; + cur.size =3D p_dir->size - exfat_dentries_to_cluster(sbi, next); + if (!(next & (sbi->dentries_per_clu - 1))) { + cur.size++; + if (exfat_chain_advance(inode->i_sb, &cur, 1)) { + ei->hint_femp.eidx =3D EXFAT_HINT_NONE; + return; + } + } + + ei->hint_femp.cur =3D cur; + ei->hint_femp.eidx =3D next; + ei->hint_femp.count =3D 0; +} + +static void exfat_invalidate_empty_hint(struct inode *inode) +{ + struct exfat_inode_info *ei =3D EXFAT_I(inode); + + ei->hint_femp.eidx =3D EXFAT_HINT_NONE; + ei->hint_femp.count =3D 0; +} + /* search EMPTY CONTINUOUS "num_entries" entries */ static int exfat_search_empty_slot(struct super_block *sb, struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir, @@ -385,6 +429,8 @@ int exfat_find_empty_entry(struct inode *inode, inode->i_blocks +=3D sbi->cluster_size >> 9; } =20 + exfat_set_next_empty_hint(inode, p_dir, dentry, num_entries, es); + p_dir->dir =3D exfat_sector_to_cluster(sbi, es->bh[0]->b_blocknr); p_dir->size -=3D dentry / sbi->dentries_per_clu; =20 @@ -516,6 +562,7 @@ static int exfat_add_entry(struct inode *inode, const c= har *path, dentry, ES_ALL_ENTRIES); if (!cleanup_ret) { exfat_remove_entries(inode, &es, ES_IDX_FILE, false); + exfat_invalidate_empty_hint(inode); cleanup_ret =3D exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); } @@ -628,7 +675,8 @@ static int exfat_find(struct inode *dir, const struct q= str *qname, ei->hint_stat.clu =3D cdir.dir; ei->hint_stat.eidx =3D 0; ei->version =3D (inode_peek_iversion_raw(dir) & 0xffffffff); - ei->hint_femp.eidx =3D EXFAT_HINT_NONE; + if (!ei->name_filter) + ei->hint_femp.eidx =3D EXFAT_HINT_NONE; } =20 /* search the file name for directories */ @@ -803,6 +851,8 @@ static int exfat_unlink(struct inode *dir, struct dentr= y *dentry) =20 /* update the directory entry */ exfat_remove_entries(inode, &es, ES_IDX_FILE, true); + exfat_invalidate_empty_hint(dir); + err =3D exfat_put_dentry_set(&es, IS_DIRSYNC(inode)); if (err) goto unlock; @@ -957,6 +1007,8 @@ static int exfat_rmdir(struct inode *dir, struct dentr= y *dentry) exfat_set_volume_dirty(sb); =20 exfat_remove_entries(inode, &es, ES_IDX_FILE, true); + exfat_invalidate_empty_hint(dir); + err =3D exfat_put_dentry_set(&es, IS_DIRSYNC(dir)); if (err) goto unlock; @@ -1138,6 +1190,7 @@ static int exfat_move_file(struct inode *parent_inode, ES_ALL_ENTRIES)) { exfat_remove_entries(parent_inode, &new_es, ES_IDX_FILE, false); + exfat_invalidate_empty_hint(parent_inode); exfat_put_dentry_set(&new_es, false); } goto put_mov_es; @@ -1214,6 +1267,7 @@ static int __exfat_rename(struct inode *old_parent_in= ode, ret =3D exfat_rename_file(new_parent_inode, &uni_name, ei); else ret =3D exfat_move_file(new_parent_inode, &uni_name, ei); + exfat_invalidate_empty_hint(old_parent_inode); if (!ret) exfat_name_filter_add(new_parent_inode, &uni_name); =20 @@ -1228,6 +1282,8 @@ static int __exfat_rename(struct inode *old_parent_in= ode, } =20 exfat_remove_entries(new_inode, &es, ES_IDX_FILE, true); + exfat_invalidate_empty_hint(new_parent_inode); + ret =3D exfat_put_dentry_set(&es, IS_DIRSYNC(new_inode)); if (ret) goto del_out; --=20 2.34.1 From nobody Sat Sep 26 10:02:10 2026 Received: from mail-pg1-f174.google.com (mail-pg1-f174.google.com [209.85.215.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7CAD4A8A21 for ; Wed, 2 Sep 2026 15:25:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362756; cv=none; b=TOQols9MWnQA1t8PbuHpq1V4bhpapAP/tQ0z6nCrMSZy/ZIYrJIINDh4Wvcg3dWzkKgjsWmME6h7D0fvIccA0aANgilxO3KAfz183gcHGpGCTLVhJD4Oqf9/l7bXEM93hwxEuwFzxReebPBiTKS+IAPYkCiQafGls8iFlBb3J8w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788362756; c=relaxed/simple; bh=08D61IpPd0WpoPdALwusfBmqrkwFkAQchYTuG+yQlTs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I9g6wPTSCTnVBjxPI0mc9p45YZRIhhnCuabyt1qJsipFlX+IdUp6fbo0CKo6KgTcMz9MSJvVP92hNjdjY7Fcu70X6V0y+MQvWngmU5d3AFqv/evq/BvONUNeXiOmDJuTi1Z/OvoQ6UbY4Qkjyy2/i17iMue/ZTUSs9NHFjXMmSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Uye3Ep65; arc=none smtp.client-ip=209.85.215.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Uye3Ep65" Received: by mail-pg1-f174.google.com with SMTP id 41be03b00d2f7-cbe6295f05bso1871483a12.1 for ; Wed, 02 Sep 2026 08:25:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788362753; x=1788967553; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=e8Z+7+qAf5Kbmwp9Q2Sbqlkuz/KNNg3sSHHKRXs8Rck=; b=Uye3Ep65Mrnc/LoEblt1v8dvn2g2FoRnhxZZ08XiPo+HRuZtC7OrfEciyMVQyN0tv+ /eUbZOdNEaUfAXY3wnZN7DAGcP1kNQiB6cjjbeoKhRLCkT8guhQoSU8DgWCy3aE+0WxS kvKdM3xfTCuvN1B81Mx78cGWhypxZJbFajq7hTtbDapD2yZBwZlhHqdT7lK8LImKqyLn hxZyMspIgITnbV4xEHA47su7ZqrFIRt8IUVthrGeE7xClZ8heqUcQuIoRLtmFAJBeMqw MFtSvlaela60M+JpmGGPfmYj4tqFD/uN3lx5HePd87g5w16M5b4U9lWjQkerhHVoz9hz +CEA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788362753; x=1788967553; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=e8Z+7+qAf5Kbmwp9Q2Sbqlkuz/KNNg3sSHHKRXs8Rck=; b=OjWLlwBbWhuDDKISX3Du0n/dxEr4Zyp2xkwUOZXPA4O9HOLJjD3qIVIbffPJ5gfDR6 r5GN996xUqAtUkfArwNdjMs6S70+RLoWIWHhifdeVQxXt5sEPMfXdlrhH/4eXpKp+NlV yaOgQR84TLBYXV7lYc/9Drxb914A9xnAYxFrvJVFCYWlN1qZCYVxtXn6VT8HbWLSkb1q zuPdDVO3exBlwxD0w60JMdyenutOt4Km7gKliIA8tlUL+/Waf88EAC6JDjZMU4yP9GGu s6zmBbJYsxw9hM40fcjwwV1FiVQDvF+KYUjROtGI5TqpOtUqYqY1h1DsHizfY5vKg4UU PjVg== X-Forwarded-Encrypted: i=1; AKwUvBxADlDOGIY4Y2Evfywtv68ma3bPYv7XoopltaAWRvQ0syzmnWA9qB5DeJEIZOwrA9ok3BUwIMeRcENBZMw=@vger.kernel.org X-Gm-Message-State: AFuF++noOwZF88Z/MuHoJOF79eGgFNGdmsxD/qb9dQWutfCipRciVRiy sVYUO+s2/0+BN2R2mCnWOB8BIwrkDSLag7dlQ2OtqydM9jHRb2J+aTYk X-Gm-Gg: AYBFou3U9a3t5iYR2OJY33Z0x2Jr63NFKAjQL9MUvOcAaLVNmWlXEsfULGMh96nvrEf kILl30AVf6XcJuxBgC+lB8auriCvicSXeJgsxi/JK9lyr+mMYXjzwifSvNynPLJsDENpRYIiEcL 41pBoKFoVHRimOFrStFEUa3qanwZqWwgRCxkhe5G8vxZ1l9t4EmdbsLC1iBoDxB1DmxW2GQoAYq ciPdZ0u6Wu2ZS7rzbJBAWz/BoNB+eI74Rlv4SCKrlT/PuEEzdm7MEyp5ysJzTDml8bL2xtg331A BHVHZ5R4x4kyO1zWvADRl2wrYuvyPJl4P18ZCoXbjwnBYRvUfIuB7FeBLpjnBDQnOcpetOczAjE 8u4xvbBp+slDAfI9RIaXHzgQqNzpVCV/uyEYNjYM/WqXfl1PG2ROb/uHrgk1mdNLKK4cIQzXWFg B+VDDj3lYEK4r6TaRakkg93ItCruDp95O+eBIVuebsrh7Zh1qHrNDRAL2T/+UBNJ5M3N7JvMymh w== X-Received: by 2002:a17:90b:5445:b0:38f:657:6823 with SMTP id 98e67ed59e1d1-39af63f7f7amr3086327a91.8.1788362753038; Wed, 02 Sep 2026 08:25:53 -0700 (PDT) Received: from localhost.localdomain ([47.100.192.162]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990bd21e9dsm12359242a91.4.2026.09.02.08.25.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 08:25:52 -0700 (PDT) From: Yang Wen To: linkinjeon@kernel.org, sj1557.seo@samsung.com, chizhiling@163.com Cc: yuezhang.mo@sony.com, exfat@lists.linux.dev, linux-kernel@vger.kernel.org, Yang Wen Subject: [PATCH v3 3/3] exfat: reclaim name filters under memory pressure Date: Wed, 2 Sep 2026 23:24:55 +0800 Message-Id: <20260902152455.1273557-4-anmuxixixi@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260902152455.1273557-1-anmuxixixi@gmail.com> References: <20260902152455.1273557-1-anmuxixixi@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Each active large directory can hold a 64 KiB name filter. Add a per-superblock LRU and shrinker so filters can be reclaimed when memory is tight. Signed-off-by: Yang Wen Reviewed-by: Chi Zhiling --- fs/exfat/dir.c | 145 +++++++++++++++++++++++++++++++++++++++++++- fs/exfat/exfat_fs.h | 10 +++ fs/exfat/super.c | 8 +++ 3 files changed, 160 insertions(+), 3 deletions(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 5145f09bcee6..c7aa50d41e9f 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -9,6 +9,7 @@ #include #include #include +#include #include =20 #include "exfat_raw.h" @@ -91,12 +92,144 @@ static void exfat_name_filter_indexes(struct super_blo= ck *sb, EXFAT_NAME_FILTER_ORDER); } =20 +static unsigned long * +exfat_name_filter_detach_locked(struct exfat_sb_info *sbi, + struct exfat_inode_info *ei) +{ + unsigned long *filter =3D ei->name_filter; + + if (!filter) + return NULL; + + ei->name_filter =3D NULL; + list_del_init(&ei->name_filter_lru); + sbi->name_filter_count--; + return filter; +} + +static void exfat_name_filter_touch(struct exfat_inode_info *ei) +{ + struct exfat_sb_info *sbi =3D EXFAT_SB(ei->vfs_inode.i_sb); + + spin_lock(&sbi->name_filter_lock); + if (ei->name_filter) + list_move_tail(&ei->name_filter_lru, &sbi->name_filter_lru); + spin_unlock(&sbi->name_filter_lock); +} + void exfat_name_filter_free(struct inode *inode) { struct exfat_inode_info *ei =3D EXFAT_I(inode); + struct exfat_sb_info *sbi; + unsigned long *filter; =20 - kvfree(ei->name_filter); - ei->name_filter =3D NULL; + if (!READ_ONCE(ei->name_filter)) + return; + + sbi =3D EXFAT_SB(inode->i_sb); + spin_lock(&sbi->name_filter_lock); + filter =3D exfat_name_filter_detach_locked(sbi, ei); + spin_unlock(&sbi->name_filter_lock); + kvfree(filter); +} + +static unsigned long +exfat_name_filter_count_objects(struct shrinker *shrinker, + struct shrink_control *sc) +{ + struct exfat_sb_info *sbi =3D shrinker->private_data; + unsigned long count; + + spin_lock(&sbi->name_filter_lock); + count =3D sbi->name_filter_count; + spin_unlock(&sbi->name_filter_lock); + + return count ? count : SHRINK_EMPTY; +} + +static unsigned long +exfat_name_filter_scan_objects(struct shrinker *shrinker, + struct shrink_control *sc) +{ + struct exfat_sb_info *sbi =3D shrinker->private_data; + unsigned long freed =3D 0; + + /* Avoid reclaim recursion from a GFP_NOFS allocation under s_lock. */ + if (!mutex_trylock(&sbi->s_lock)) { + sc->nr_scanned =3D 0; + return SHRINK_STOP; + } + + while (freed < sc->nr_to_scan) { + struct exfat_inode_info *ei; + unsigned long *filter; + + spin_lock(&sbi->name_filter_lock); + if (list_empty(&sbi->name_filter_lru)) { + spin_unlock(&sbi->name_filter_lock); + break; + } + + ei =3D list_first_entry(&sbi->name_filter_lru, + struct exfat_inode_info, + name_filter_lru); + filter =3D exfat_name_filter_detach_locked(sbi, ei); + spin_unlock(&sbi->name_filter_lock); + + kvfree(filter); + freed++; + cond_resched(); + } + + mutex_unlock(&sbi->s_lock); + sc->nr_scanned =3D freed; + return freed; +} + +void exfat_name_filter_shrinker_register(struct super_block *sb) +{ + struct exfat_sb_info *sbi =3D EXFAT_SB(sb); + struct shrinker *shrinker; + + shrinker =3D shrinker_alloc(SHRINKER_NONSLAB, + "exfat-name-filter:%s", sb->s_id); + if (!shrinker) { + exfat_warn(sb, "failed to allocate name filter shrinker"); + return; + } + + shrinker->count_objects =3D exfat_name_filter_count_objects; + shrinker->scan_objects =3D exfat_name_filter_scan_objects; + shrinker->private_data =3D sbi; + shrinker_register(shrinker); + sbi->name_filter_shrinker =3D shrinker; +} + +void exfat_name_filter_shrinker_unregister(struct super_block *sb) +{ + struct exfat_sb_info *sbi =3D EXFAT_SB(sb); + struct shrinker *shrinker =3D sbi->name_filter_shrinker; + + sbi->name_filter_shrinker =3D NULL; + shrinker_free(shrinker); + + for (;;) { + struct exfat_inode_info *ei; + unsigned long *filter; + + spin_lock(&sbi->name_filter_lock); + if (list_empty(&sbi->name_filter_lru)) { + spin_unlock(&sbi->name_filter_lock); + break; + } + + ei =3D list_first_entry(&sbi->name_filter_lru, + struct exfat_inode_info, + name_filter_lru); + filter =3D exfat_name_filter_detach_locked(sbi, ei); + spin_unlock(&sbi->name_filter_lock); + kvfree(filter); + } } =20 bool exfat_name_filter_maybe_contains(struct inode *inode, @@ -108,6 +241,7 @@ bool exfat_name_filter_maybe_contains(struct inode *ino= de, if (!ei->name_filter) return true; =20 + exfat_name_filter_touch(ei); exfat_name_filter_indexes(inode->i_sb, name, indexes); return test_bit(indexes[0], ei->name_filter) && test_bit(indexes[1], ei->name_filter) && @@ -123,6 +257,7 @@ void exfat_name_filter_add(struct inode *inode, if (!ei->name_filter) return; =20 + exfat_name_filter_touch(ei); exfat_name_filter_indexes(inode->i_sb, name, indexes); __set_bit(indexes[0], ei->name_filter); __set_bit(indexes[1], ei->name_filter); @@ -145,7 +280,7 @@ static void exfat_build_name_filter(struct super_block = *sb, struct exfat_sb_info *sbi =3D EXFAT_SB(sb); int i; =20 - if (ei->name_filter || + if (!sbi->name_filter_shrinker || ei->name_filter || exfat_bytes_to_dentries(i_size_read(inode)) < EXFAT_NAME_FILTER_MIN_DENTRIES) return; @@ -195,7 +330,11 @@ static void exfat_build_name_filter(struct super_block= *sb, } =20 complete: + spin_lock(&sbi->name_filter_lock); ei->name_filter =3D filter; + list_add_tail(&ei->name_filter_lru, &sbi->name_filter_lru); + sbi->name_filter_count++; + spin_unlock(&sbi->name_filter_lock); return; abort: kvfree(filter); diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 5187dee47cf1..b81cfbdd117f 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -14,6 +14,8 @@ #include #include =20 +struct shrinker; + #define EXFAT_ROOT_INO 1 =20 /* @@ -260,6 +262,11 @@ struct exfat_sb_info { =20 spinlock_t inode_hash_lock; struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; + /* Protects name_filter_lru and name_filter_count. */ + spinlock_t name_filter_lock; + struct list_head name_filter_lru; + unsigned long name_filter_count; + struct shrinker *name_filter_shrinker; struct rcu_head rcu; }; =20 @@ -291,6 +298,7 @@ struct exfat_inode_info { struct exfat_hint_femp hint_femp; /* Complete, in-memory Bloom filter of directory names */ unsigned long *name_filter; + struct list_head name_filter_lru; =20 spinlock_t cache_lru_lock; struct list_head cache_lru; @@ -631,6 +639,8 @@ bool exfat_name_filter_maybe_contains(struct inode *ino= de, void exfat_name_filter_add(struct inode *inode, const struct exfat_uni_name *name); void exfat_name_filter_free(struct inode *inode); +void exfat_name_filter_shrinker_register(struct super_block *sb); +void exfat_name_filter_shrinker_unregister(struct super_block *sb); =20 static inline int exfat_chain_advance(struct super_block *sb, struct exfat_chain *chain, unsigned int step) diff --git a/fs/exfat/super.c b/fs/exfat/super.c index f73cf2d02365..3f9d880ec363 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -45,6 +45,7 @@ static void exfat_put_super(struct super_block *sb) { struct exfat_sb_info *sbi =3D EXFAT_SB(sb); =20 + exfat_name_filter_shrinker_unregister(sb); mutex_lock(&sbi->s_lock); exfat_clear_volume_dirty(sb); exfat_free_bitmap(sbi); @@ -196,6 +197,7 @@ static struct inode *exfat_alloc_inode(struct super_blo= ck *sb) return NULL; =20 ei->name_filter =3D NULL; + INIT_LIST_HEAD(&ei->name_filter_lru); return &ei->vfs_inode; } =20 @@ -732,6 +734,8 @@ static int exfat_fill_super(struct super_block *sb, str= uct fs_context *fc) goto free_table; } =20 + exfat_name_filter_shrinker_register(sb); + return 0; =20 put_inode: @@ -827,6 +831,10 @@ static int exfat_init_fs_context(struct fs_context *fc) =20 mutex_init(&sbi->s_lock); mutex_init(&sbi->bitmap_lock); + spin_lock_init(&sbi->name_filter_lock); + INIT_LIST_HEAD(&sbi->name_filter_lru); + sbi->name_filter_count =3D 0; + sbi->name_filter_shrinker =3D NULL; ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); =20 --=20 2.34.1