From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from out30-111.freemail.mail.aliyun.com (out30-111.freemail.mail.aliyun.com [115.124.30.111]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3734919B5B1; Wed, 7 Jan 2026 06:56:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.111 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767769006; cv=none; b=V9w+G14apPefpUWmvaoZNliXg1dufkKD7tLsEo8hApYpuIeCqD6q/sGFSqbtm22cOwdpwdlx5LRVbqLuMYrdPea6QcnB3QeHsRwVu1uiSboH+Apn/VJrfVKFPn4o1fFfvZAJj4eVQ8h8Sth3nPat1PtjVUr5VbGacekqdrYAvss= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767769006; c=relaxed/simple; bh=8ukWQgj/Fy5sAs8mIv+gu+KurqiIbrTWE0ETo3qoPt4=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=J3Cfq1pRp/K3/8jNDjcHK3airP0uCS3iPgJ4dZtMxLCP8sedpTiXhwGfQGbgOtEdxkw7tldqXRA251kQ5W7DMDp3Gh+PNzuXDKGeCvIJNsYarnSRbdnqyySJCiENo8BLiGv3ysuf+QUtuLUfkhSQYOV1u+bHfpyxOCXfpTAOuFY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=OoBA9hPF; arc=none smtp.client-ip=115.124.30.111 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="OoBA9hPF" DKIM-Signature:v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1767768998; h=Message-ID:Date:MIME-Version:Subject:To:From:Content-Type; bh=uxQJ/2PmUTK2PkMYEMI1zPFooDQNSjqfmXM9b4dv9MU=; b=OoBA9hPFdwKCQyytg8wGFLHEWpqsLY2gqThsa+2+wjRE29Fsj6Qnl9CadLFhtflVmUTfp4d6V6FNVU3PZj0T3/oSPgXWwwkHgNlAI67lRJRAnCuMJ56pkJZA81lArWSuHd9vAxwP/r/ypigxBSbdK2nD1nRFauxoq3elowy62pE= Received: from 30.221.132.240(mailfrom:hsiangkao@linux.alibaba.com fp:SMTPD_---0WwXwQlH_1767768997 cluster:ay36) by smtp.aliyun-inc.com; Wed, 07 Jan 2026 14:56:37 +0800 Message-ID: Date: Wed, 7 Jan 2026 14:56:35 +0800 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v12 07/10] erofs: introduce the page cache share feature To: Hongbo Li Cc: djwong@kernel.org, amir73il@gmail.com, hch@lst.de, linux-fsdevel@vger.kernel.org, linux-erofs@lists.ozlabs.org, linux-kernel@vger.kernel.org, Chao Yu , brauner@kernel.org References: <20251231090118.541061-1-lihongbo22@huawei.com> <20251231090118.541061-8-lihongbo22@huawei.com> <99a517aa-744b-487b-bce8-294b69a0cd50@linux.alibaba.com> From: Gao Xiang In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit On 2026/1/7 14:48, Hongbo Li wrote: > Hi, Xiang > > On 2026/1/7 14:08, Gao Xiang wrote: >> >> >> On 2025/12/31 17:01, Hongbo Li wrote: >>> From: Hongzhen Luo >>> >>> Currently, reading files with different paths (or names) but the same >>> content will consume multiple copies of the page cache, even if the >>> content of these page caches is the same. For example, reading >>> identical files (e.g., *.so files) from two different minor versions of >>> container images will cost multiple copies of the same page cache, >>> since different containers have different mount points. Therefore, >>> sharing the page cache for files with the same content can save memory. >>> >>> This introduces the page cache share feature in erofs. It allocate a >>> deduplicated inode and use its page cache as shared. Reads for files >>> with identical content will ultimately be routed to the page cache of >>> the deduplicated inode. In this way, a single page cache satisfies >>> multiple read requests for different files with the same contents. >>> >>> We introduce inode_share mount option to enable the page sharing mode >>> during mounting. >>> >>> Signed-off-by: Hongzhen Luo >>> Signed-off-by: Hongbo Li >>> --- >>>   Documentation/filesystems/erofs.rst |   5 + >>>   fs/erofs/Makefile                   |   1 + >>>   fs/erofs/internal.h                 |  31 +++++ >>>   fs/erofs/ishare.c                   | 170 ++++++++++++++++++++++++++++ >>>   fs/erofs/super.c                    |  55 ++++++++- >>>   fs/erofs/xattr.c                    |  34 ++++++ >>>   fs/erofs/xattr.h                    |   3 + >>>   7 files changed, 297 insertions(+), 2 deletions(-) >>>   create mode 100644 fs/erofs/ishare.c >>> >>> diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst >>> index 08194f194b94..27d3caa3c73c 100644 >>> --- a/Documentation/filesystems/erofs.rst >>> +++ b/Documentation/filesystems/erofs.rst >>> @@ -128,7 +128,12 @@ device=%s              Specify a path to an extra device to be used together. >>>   fsid=%s                Specify a filesystem image ID for Fscache back-end. >>>   domain_id=%s           Specify a domain ID in fscache mode so that different images >>>                          with the same blobs under a given domain ID can share storage. >>> +                       Also used for inode page sharing mode which defines a sharing >>> +                       domain. >>>   fsoffset=%llu          Specify block-aligned filesystem offset for the primary device. >>> +inode_share            Enable inode page sharing for this filesystem.  Inodes with >>> +                       identical content within the same domain ID can share the >>> +                       page cache. >>>   =================== ========================================================= >>>   Sysfs Entries >>> diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile >>> index 549abc424763..a80e1762b607 100644 >>> --- a/fs/erofs/Makefile >>> +++ b/fs/erofs/Makefile >>> @@ -10,3 +10,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o >>>   erofs-$(CONFIG_EROFS_FS_ZIP_ACCEL) += decompressor_crypto.o >>>   erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o >>>   erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o >>> +erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o >>> diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h >>> index ec79e8b44d3b..6ef1cdd9d651 100644 >>> --- a/fs/erofs/internal.h >>> +++ b/fs/erofs/internal.h >>> @@ -179,6 +179,7 @@ struct erofs_sb_info { >>>   #define EROFS_MOUNT_DAX_ALWAYS        0x00000040 >>>   #define EROFS_MOUNT_DAX_NEVER        0x00000080 >>>   #define EROFS_MOUNT_DIRECT_IO        0x00000100 >>> +#define EROFS_MOUNT_INODE_SHARE        0x00000200 >>>   #define clear_opt(opt, option)    ((opt)->mount_opt &= ~EROFS_MOUNT_##option) >>>   #define set_opt(opt, option)    ((opt)->mount_opt |= EROFS_MOUNT_##option) >>> @@ -269,6 +270,11 @@ static inline u64 erofs_nid_to_ino64(struct erofs_sb_info *sbi, erofs_nid_t nid) >>>   /* default readahead size of directories */ >>>   #define EROFS_DIR_RA_BYTES    16384 >>> +struct erofs_inode_fingerprint { >>> +    u8 *opaque; >>> +    int size; >>> +}; >>> + >>>   struct erofs_inode { >>>       erofs_nid_t nid; >>> @@ -304,6 +310,18 @@ struct erofs_inode { >>>           }; >>>   #endif    /* CONFIG_EROFS_FS_ZIP */ >>>       }; >>> +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE >>> +    struct list_head ishare_list; >>> +    union { >>> +        /* for each anon shared inode */ >>> +        struct { >>> +            struct erofs_inode_fingerprint fingerprint; >>> +            spinlock_t ishare_lock; >>> +        }; >>> +        /* for each real inode */ >>> +        struct inode *sharedinode; >>> +    }; >>> +#endif >>>       /* the corresponding vfs inode */ >>>       struct inode vfs_inode; >>>   }; >>> @@ -410,6 +428,7 @@ extern const struct inode_operations erofs_dir_iops; >>>   extern const struct file_operations erofs_file_fops; >>>   extern const struct file_operations erofs_dir_fops; >>> +extern const struct file_operations erofs_ishare_fops; >>>   extern const struct iomap_ops z_erofs_iomap_report_ops; >>> @@ -541,6 +560,18 @@ static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) { >>>   static inline void erofs_fscache_submit_bio(struct bio *bio) {} >>>   #endif >>> +#ifdef CONFIG_EROFS_FS_PAGE_CACHE_SHARE >>> +int __init erofs_init_ishare(void); >>> +void erofs_exit_ishare(void); >>> +bool erofs_ishare_fill_inode(struct inode *inode); >>> +void erofs_ishare_free_inode(struct inode *inode); >>> +#else >>> +static inline int erofs_init_ishare(void) { return 0; } >>> +static inline void erofs_exit_ishare(void) {} >>> +static inline bool erofs_ishare_fill_inode(struct inode *inode) { return false; } >>> +static inline void erofs_ishare_free_inode(struct inode *inode) {} >>> +#endif // CONFIG_EROFS_FS_PAGE_CACHE_SHARE >>> + >>>   long erofs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); >>>   long erofs_compat_ioctl(struct file *filp, unsigned int cmd, >>>               unsigned long arg); >>> diff --git a/fs/erofs/ishare.c b/fs/erofs/ishare.c >>> new file mode 100644 >>> index 000000000000..e93d379d4a3a >>> --- /dev/null >>> +++ b/fs/erofs/ishare.c >>> @@ -0,0 +1,170 @@ >>> +// SPDX-License-Identifier: GPL-2.0-or-later >>> +/* >>> + * Copyright (C) 2024, Alibaba Cloud >>> + */ >>> +#include >>> +#include >>> +#include "internal.h" >>> +#include "xattr.h" >>> + >>> +#include "../internal.h" >>> + >>> +static struct vfsmount *erofs_ishare_mnt; >>> + >>> +static int erofs_ishare_iget5_eq(struct inode *inode, void *data) >>> +{ >>> +    struct erofs_inode_fingerprint *fp1 = &EROFS_I(inode)->fingerprint; >>> +    struct erofs_inode_fingerprint *fp2 = data; >>> + >>> +    return fp1->size == fp2->size && >>> +        !memcmp(fp1->opaque, fp2->opaque, fp2->size); >>> +} >>> + >>> +static int erofs_ishare_iget5_set(struct inode *inode, void *data) >>> +{ >>> +    struct erofs_inode *vi = EROFS_I(inode); >>> + >>> +    vi->fingerprint = *(struct erofs_inode_fingerprint *)data; >>> +    INIT_LIST_HEAD(&vi->ishare_list); >>> +    spin_lock_init(&vi->ishare_lock); >>> +    return 0; >>> +} >>> + >>> +bool erofs_ishare_fill_inode(struct inode *inode) >>> +{ >>> +    struct erofs_sb_info *sbi = EROFS_SB(inode->i_sb); >>> +    struct erofs_inode *vi = EROFS_I(inode); >>> +    struct erofs_inode_fingerprint fp; >>> +    struct inode *sharedinode; >>> +    unsigned long hash; >>> + >>> +    if (!test_opt(&sbi->opt, INODE_SHARE)) >>> +        return false; >>> +    (void)erofs_xattr_fill_ishare_fp(&fp, inode, sbi->domain_id); >>> +    if (!fp.size) >>> +        return false; >> >> Why not just: >> >>      if (erofs_xattr_fill_ishare_fp(&fp, inode, sbi->domain_id)) >>          return false; >> > > When erofs_sb_has_ishare_xattrs returns false, erofs_xattr_fill_ishare_fp also considers success. Then why !test_opt(&sbi->opt, INODE_SHARE) didn't return? Thanks, Gao Xiang