From nobody Sat Sep 26 22:55:58 2026 Received: from mail-pj1-f50.google.com (mail-pj1-f50.google.com [209.85.216.50]) (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 5BC55448D1F for ; Fri, 28 Aug 2026 14:59:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787929144; cv=none; b=fGf0EQMOSj2KWu/mTa8LfgK9zldqDwNIGhuBAogzgIWWcqKqhEt71nmNDX3AGdt3jPsbIH9Bfo1yJM3l1kMMayQRi1RJaNYUCnt1X+t7w8dpcxGx0sjk7GIionLKJfbTI6uePbPbW2p4LjkaStTjKAZXmP6eh/aqL4/v/nZ8gJw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787929144; c=relaxed/simple; bh=WAC0BZyf3XDU8BREQrOa1Ssc+2WwO8TMI0W5W0RmrAc=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=dmgFyqxJSzTKjxmqoCShmR7T+vfmXGyTXYa25DxhIkqVKAxVMerWfZJDOkAxrqR22k01bb50nu6X/BHxUS+KMaa89OI1Xv6WY1LPoI8BOPE5weVcrY46aJfgsFUxkYqgqcA7cymhFf2AM3JarU4OmHuIMobkpxkDBcStQBSgT80= 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=bfVaq86w; arc=none smtp.client-ip=209.85.216.50 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="bfVaq86w" Received: by mail-pj1-f50.google.com with SMTP id 98e67ed59e1d1-38dfe7eb825so1004222a91.0 for ; Fri, 28 Aug 2026 07:59:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787929142; x=1788533942; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=r7xqH8BpeNFukXywkuuUrWsBTL6ZriYdy/ftH6jbRUI=; b=bfVaq86wgQ6IbstwT7rvayJXennWgkvqNqqI75aUhNEnJe1ZjumfCjWxAaBx4sRkot 1XdBOi+Q+Mv+rYzxGRT5upMJbMRk445aeoFEIuhPqoZLb8wc1p6jgn4Zho6WpUbxK5Is ucCTOBVyHnjlyGRioIU7k28qyzyIvpkVxPEwTykWvYHxHN7z0HYjJzamMHTVV0jcdhFN DDf6e22lxS/ixX+b5ZUg8Ybp4TgSC4/WVR9E+uuiDeAsB/85xFK0fy2KzPCGtgTKwtac XAGniq664Ibx3KGKN9kfctyj66r+SL5DOiYhhzzd4yrSXmwbU32L2z5X70g+HC/2QIfA LjVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787929142; x=1788533942; h=content-transfer-encoding:mime-version: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=r7xqH8BpeNFukXywkuuUrWsBTL6ZriYdy/ftH6jbRUI=; b=fZ//NG3mRsT7LOpSMKre3UN2Bd/1Xd3WbSJv5myaoBK6or8YgG6ZrhYAJ3Kw7847OR PYh6BFR3AQA9P8crXg90qLxHMF9Gc/zSPuf7AfXU7/hQlKOlxv4c37KoquJd1A5j9cWD uGx5LcdaqdcNaIs1kaDMHRqtfusBdOLvE8Tb2CkoZ/HWQE74ivR2ue/xxg8Ohbk1EJMM 4uKFGmOBUE+G1O1Dw2FNdnwi/bp4ECdHN9hCifLOkGXMCwW4LbLjgHLiANPgqPbP8Mrp vD+wdO2B4q8zC/g1KN38YN1Gx1HbBnwUOd2MI8b1SObOWy4ma/PVlI/JP+9uY+xD79kO pq/w== X-Forwarded-Encrypted: i=1; AHgh+RoJn3PHWE0E4Bl1KFIZXB2x6kT+u+Kw7bnbmTX/RibCagvmd1BFu6pFuBE7nahGbkYZJHDux2aXmqzrszU=@vger.kernel.org X-Gm-Message-State: AFuF++m1t0yhmc9CgQzC5EZk3sKn4FWXwow+oO+kt/XjBz8noytIcwea H2YwsapfjUSg/5cDzrmzSlQ6Ae349OYW9VPgWSa6GzShv2SJPPVBUOy1 X-Gm-Gg: AR+sD13Upli1vsVOXB8v6yzQzJP5cLP7/r9dIlvRlw0o7hyDkp39JJXzRYrja7zhrqd D64Ez0Gv/PKlMRSl2l4Qg9zrn03Aty2dnV/5UkYro7NooLXL5Qa+b6RUjnlQyo2MZFAlxs+ElV0 OgDn8GqdoELW1jN0TBMsScl85B/5APCRwj6kYMIPa5bRiv58vn359dTVy1eOu25U6TCte9ssZpS MvFUGFbDhAccEhOYbxCS/qD6UWpN/L9aZOJ0GU0GB2KJPK6BAiOlabMLXX7VjhbpFuVl0F+wIzM DACdCJ4AidG9wmZOb2if7Ta2JD6rwTrVHVlraY5a42bJFJwwqvU0dX/iVkrudIDp2v+tRrrFrtt kyNN04wDZoQfmX0YNBQ4wyF5Oo6wv6W/WZf6UBXLAtWqQ82mR5WjpALegtLNVXRauMDZ+ijmvBL 268OaO8NXGp6ILGTI85DwQNH2NIDoTEMcVp/UehXH/39usbUOhjNDzgGx/CneU2xbrfF60s/pup g== X-Received: by 2002:a17:90b:3c8a:b0:38e:524:8797 with SMTP id 98e67ed59e1d1-396d10076e9mr15750246a91.13.1787929141588; Fri, 28 Aug 2026 07:59:01 -0700 (PDT) Received: from localhost.localdomain ([47.100.192.162]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-396b1992435sm7926670a91.13.2026.08.28.07.58.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Aug 2026 07:59:01 -0700 (PDT) From: Yang Wen To: linkinjeon@kernel.org, sj1557.seo@samsung.com Cc: yuezhang.mo@sony.com, exfat@lists.linux.dev, linux-kernel@vger.kernel.org, Yang Wen Subject: [PATCH] exfat: speed up file creation in large directories Date: Fri, 28 Aug 2026 22:58:38 +0800 Message-Id: <20260828145838.1187356-1-anmuxixixi@gmail.com> X-Mailer: git-send-email 2.34.1 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 file creation approach O(N^2). Add a 64 KiB per-directory Bloom filter and retain the next empty entry found during directory scans. A definite Bloom filter miss skips the directory scan, while a possible match falls back to the normal lookup path, preserving correctness in the presence of hash collisions. To avoid overhead for small directories, build the filter lazily when a directory reaches 1024 on-disk directory entries. A filter reclaimed under memory pressure is rebuilt on demand. Manage the filters using a per-superblock LRU and shrinker so that filters for inactive directories can be reclaimed under memory pressure. Test environment: QEMU TCG multi-thread, 4 vCPUs, 6 GiB RAM 4 GiB exFAT image, 32 KiB clusters Test script: mount -t exfat /dev/vda /mnt/test time sh -c ' i=3D0 while [ "$i" -lt 20000 ]; do : > "/mnt/test/f.$i" || exit 1 i=3D$((i + 1)) done ' The measured results were: Before After real 589.48 s 5.33 s user 4.72 s 2.28 s sys 584.63 s 3.03 s The elapsed time was reduced by 99.10%, from 589.48 seconds to 5.33 seconds, corresponding to a 110.6-fold speedup. Signed-off-by: Yang Wen --- fs/exfat/dir.c | 280 ++++++++++++++++++++++++++++++++++++++++++++ fs/exfat/exfat_fs.h | 22 ++++ fs/exfat/namei.c | 44 ++++++- fs/exfat/super.c | 10 ++ 4 files changed, 355 insertions(+), 1 deletion(-) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index fe73b1380c5d..6df74bcab4b5 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include =20 #include "exfat_raw.h" #include "exfat_fs.h" @@ -65,6 +68,279 @@ 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); +} + +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; + + 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); + } +} + +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_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) && + 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_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); + __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 (!sbi->name_filter_shrinker || 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 < MAX_NAME_LENGTH && name.name[len]; len++) + ; + if (!len || len =3D=3D 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: + 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); +} + /* 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 +1268,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 +1431,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..bc4e2213c4a7 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 /* @@ -120,6 +122,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 @@ -255,6 +262,10 @@ struct exfat_sb_info { =20 spinlock_t inode_hash_lock; struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; + 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 @@ -284,6 +295,9 @@ 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; + struct list_head name_filter_lru; =20 spinlock_t cache_lru_lock; struct list_head cache_lru; @@ -619,6 +633,14 @@ 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); +void exfat_name_filter_shrinker_register(struct super_block *sb); +void exfat_name_filter_shrinker_unregister(struct super_block *sb); + static inline int exfat_chain_advance(struct super_block *sb, struct exfat_chain *chain, unsigned int step) { diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index a4dc83b5949c..80a72d24d394 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -199,6 +199,42 @@ 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; +} + /* 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 +421,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 @@ -526,6 +564,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 @@ -627,7 +666,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 */ @@ -1215,6 +1255,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; diff --git a/fs/exfat/super.c b/fs/exfat/super.c index a9ea36ba2693..28b55618c6f5 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); @@ -195,11 +196,14 @@ static struct inode *exfat_alloc_inode(struct super_b= lock *sb) if (!ei) return NULL; =20 + ei->name_filter =3D NULL; + INIT_LIST_HEAD(&ei->name_filter_lru); return &ei->vfs_inode; } =20 static void exfat_free_inode(struct inode *inode) { + exfat_name_filter_free(inode); kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); } =20 @@ -731,6 +735,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: @@ -826,6 +832,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