From c496318bc0a5780118bceb69493163db23a33f1a Mon Sep 17 00:00:00 2001 From: Jingbo Xu Date: Tue, 22 Nov 2022 16:43:39 +0800 Subject: [PATCH 1/8] anolis: erofs: fix the name of erofs_fscache_super_index_def ANBZ: #3234 Give the right name to erofs_fscache_super_index_def. Fixes: 63ed7d0d74e9 ("erofs: add fscache context helper functions") Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index 089fd90e45f5..eaff3589bbc5 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -26,7 +26,7 @@ const struct fscache_cookie_def erofs_fscache_super_index_def = { }; const struct fscache_cookie_def erofs_fscache_inode_object_def = { - .name = "CIFS.uniqueid", + .name = "EROFS.uniqueid", .type = FSCACHE_COOKIE_TYPE_DATAFILE, }; -- Gitee From 37095be6198dcbab52c993029f25aa1e5aa66741 Mon Sep 17 00:00:00 2001 From: Jia Zhu Date: Sun, 18 Sep 2022 12:34:52 +0800 Subject: [PATCH 2/8] erofs: code clean up for fscache ANBZ: #3234 commit e1de2da0b7ac2dc0120c2ba8c7044788611933ea upstream. Some cleanups. No logic changes. Suggested-by: Jingbo Xu Signed-off-by: Jia Zhu Reviewed-by: Jingbo Xu Link: https://lore.kernel.org/r/20220918043456.147-3-zhujia.zj@bytedance.com Signed-off-by: Gao Xiang [jingbo: still use erofs_fscache_unregister_cookie in erofs_put_super] Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 39 +++++++++++++++++++-------------------- fs/erofs/internal.h | 19 +++++++++---------- fs/erofs/super.c | 21 +++++++++------------ 3 files changed, 37 insertions(+), 42 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index eaff3589bbc5..d6f75f9c13c0 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -268,9 +268,8 @@ const struct address_space_operations erofs_fscache_access_aops = { .invalidatepage = erofs_fscache_invalidate_page, }; -int erofs_fscache_register_cookie(struct super_block *sb, - struct erofs_fscache **fscache, - char *name, bool need_inode) +struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, + char *name, bool need_inode) { struct erofs_fscache *ctx; struct fscache_cookie *cookie; @@ -278,7 +277,7 @@ int erofs_fscache_register_cookie(struct super_block *sb, ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cookie = fscache_acquire_cookie(EROFS_SB(sb)->volume, &erofs_fscache_inode_object_def, @@ -310,34 +309,25 @@ int erofs_fscache_register_cookie(struct super_block *sb, ctx->inode = inode; } - *fscache = ctx; - return 0; + return ctx; err_cookie: // fscache_unuse_cookie(ctx->cookie, NULL, NULL); fscache_relinquish_cookie(ctx->cookie, NULL, false); - ctx->cookie = NULL; err: kfree(ctx); - return ret; + return ERR_PTR(ret); } -void erofs_fscache_unregister_cookie(struct erofs_fscache **fscache) +void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) { - struct erofs_fscache *ctx = *fscache; - if (!ctx) return; //fscache_unuse_cookie(ctx->cookie, NULL, NULL); fscache_relinquish_cookie(ctx->cookie, NULL, false); - ctx->cookie = NULL; - iput(ctx->inode); - ctx->inode = NULL; - kfree(ctx); - *fscache = NULL; } int erofs_fscache_register_fs(struct super_block *sb) @@ -345,8 +335,8 @@ int erofs_fscache_register_fs(struct super_block *sb) struct erofs_sb_info *sbi = EROFS_SB(sb); struct fscache_cookie *volume; + struct erofs_fscache *fscache; char *name; - int ret = 0; name = kasprintf(GFP_KERNEL, "erofs,%s", sbi->opt.fsid); if (!name) @@ -357,18 +347,27 @@ int erofs_fscache_register_fs(struct super_block *sb) NULL, 0, NULL, 0, true); if (IS_ERR_OR_NULL(volume)) { erofs_err(sb, "failed to register volume for %s", name); - ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; - volume = NULL; + kfree(name); + return volume ? PTR_ERR(volume) : -EOPNOTSUPP; } sbi->volume = volume; kfree(name); - return ret; + + fscache = erofs_fscache_register_cookie(sb, sbi->opt.fsid, true); + /* acquired volume will be relinquished in kill_sb() */ + if (IS_ERR(fscache)) + return PTR_ERR(fscache); + + sbi->s_fscache = fscache; + return 0; } void erofs_fscache_unregister_fs(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); + erofs_fscache_unregister_cookie(sbi->s_fscache); fscache_relinquish_cookie(sbi->volume, NULL, false); + sbi->s_fscache = NULL; sbi->volume = NULL; } diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 24e33fe1e90b..de945aba1674 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -521,10 +521,9 @@ void erofs_fscache_unregister(void); int erofs_fscache_register_fs(struct super_block *sb); void erofs_fscache_unregister_fs(struct super_block *sb); -int erofs_fscache_register_cookie(struct super_block *sb, - struct erofs_fscache **fscache, - char *name, bool need_inode); -void erofs_fscache_unregister_cookie(struct erofs_fscache **fscache); +struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, + char *name, bool need_inode); +void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache); extern const struct address_space_operations erofs_fscache_access_aops; #else static inline int erofs_fscache_register(void) @@ -534,18 +533,18 @@ static inline int erofs_fscache_register(void) static inline void erofs_fscache_unregister(void) {} static inline int erofs_fscache_register_fs(struct super_block *sb) { - return 0; + return -EOPNOTSUPP; } static inline void erofs_fscache_unregister_fs(struct super_block *sb) {} -static inline int erofs_fscache_register_cookie(struct super_block *sb, - struct erofs_fscache **fscache, - char *name, bool need_inode) +static inline +struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, + char *name, bool need_inode) { - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } -static inline void erofs_fscache_unregister_cookie(struct erofs_fscache **fscache) +static inline void erofs_fscache_unregister_cookie(struct erofs_fscache *fscache) { } #endif diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 3e05b7ac3726..bdcd7452d882 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -127,10 +127,10 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, struct erofs_device_info *dif, erofs_off_t *pos) { struct erofs_sb_info *sbi = EROFS_SB(sb); + struct erofs_fscache *fscache; struct erofs_deviceslot *dis; struct block_device *bdev; void *ptr; - int ret; ptr = erofs_read_metabuf(buf, sb, erofs_blknr(*pos), EROFS_KMAP); if (IS_ERR(ptr)) @@ -148,10 +148,10 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, } if (erofs_is_fscache_mode(sb)) { - ret = erofs_fscache_register_cookie(sb, &dif->fscache, - dif->path, false); - if (ret) - return ret; + fscache = erofs_fscache_register_cookie(sb, dif->path, false); + if (IS_ERR(fscache)) + return PTR_ERR(fscache); + dif->fscache = fscache; } else { bdev = blkdev_get_by_path(dif->path, FMODE_READ | FMODE_EXCL, sb->s_type); @@ -514,10 +514,6 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) err = erofs_fscache_register_fs(sb); if (err) return err; - err = erofs_fscache_register_cookie(sb, &sbi->s_fscache, - sbi->opt.fsid, true); - if (err) - return err; err = super_setup_bdi(sb); if (err) @@ -619,7 +615,8 @@ static int erofs_release_device_info(int id, void *ptr, void *data) if (dif->bdev) blkdev_put(dif->bdev, FMODE_READ | FMODE_EXCL); - erofs_fscache_unregister_cookie(&dif->fscache); + erofs_fscache_unregister_cookie(dif->fscache); + dif->fscache = NULL; kfree(dif->path); kfree(dif); return 0; @@ -689,7 +686,6 @@ static void erofs_kill_sb(struct super_block *sb) if (!sbi) return; erofs_free_dev_context(sbi->devs); - erofs_fscache_unregister_cookie(&sbi->s_fscache); erofs_fscache_unregister_fs(sb); kfree(sbi->opt.fsid); kfree(sbi); @@ -708,7 +704,8 @@ static void erofs_put_super(struct super_block *sb) iput(sbi->managed_cache); sbi->managed_cache = NULL; #endif - erofs_fscache_unregister_cookie(&sbi->s_fscache); + erofs_fscache_unregister_cookie(sbi->s_fscache); + sbi->s_fscache = NULL; } static struct file_system_type erofs_fs_type = { -- Gitee From fe420fa615f7f0b1ee13bce98931b9071eed93b5 Mon Sep 17 00:00:00 2001 From: Jia Zhu Date: Sun, 18 Sep 2022 12:34:53 +0800 Subject: [PATCH 3/8] erofs: introduce fscache-based domain ANBZ: #3234 commit 8b7adf1dff3d5baf687acda936f193f80b7e0179 upstream. A new fscache-based shared domain mode is going to be introduced for erofs. In which case, same data blobs in same domain will be shared and reused to reduce on-disk space usage. The implementation of sharing blobs will be introduced in subsequent patches. Signed-off-by: Jia Zhu Reviewed-by: Jingbo Xu Link: https://lore.kernel.org/r/20220918043456.147-4-zhujia.zj@bytedance.com Signed-off-by: Gao Xiang Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 133 +++++++++++++++++++++++++++++++++++++------- fs/erofs/internal.h | 9 +++ 2 files changed, 123 insertions(+), 19 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index d6f75f9c13c0..e6e68436a493 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -1,9 +1,13 @@ /* * Copyright (C) 2022, Alibaba Cloud + * Copyright (C) 2022, Bytedance Inc. All rights reserved. */ #include #include "internal.h" +static DEFINE_MUTEX(erofs_domain_list_lock); +static LIST_HEAD(erofs_domain_list); + struct fscache_netfs erofs_fscache_netfs = { .name = "erofs", .version = 0, @@ -268,6 +272,101 @@ const struct address_space_operations erofs_fscache_access_aops = { .invalidatepage = erofs_fscache_invalidate_page, }; +static void erofs_fscache_domain_put(struct erofs_domain *domain) +{ + if (!domain) + return; + mutex_lock(&erofs_domain_list_lock); + if (refcount_dec_and_test(&domain->ref)) { + list_del(&domain->list); + mutex_unlock(&erofs_domain_list_lock); + fscache_relinquish_cookie(domain->volume, NULL, false); + kfree(domain->domain_id); + kfree(domain); + return; + } + mutex_unlock(&erofs_domain_list_lock); +} + +static int erofs_fscache_register_volume(struct super_block *sb) +{ + struct erofs_sb_info *sbi = EROFS_SB(sb); + char *domain_id = sbi->opt.domain_id; + struct fscache_cookie *volume; + char *name; + int ret = 0; + + name = kasprintf(GFP_KERNEL, "erofs,%s", + domain_id ? domain_id : sbi->opt.fsid); + if (!name) + return -ENOMEM; + + volume = fscache_acquire_cookie(erofs_fscache_netfs.primary_index, + &erofs_fscache_super_index_def, name, strlen(name), + NULL, 0, NULL, 0, true); + if (IS_ERR_OR_NULL(volume)) { + erofs_err(sb, "failed to register volume for %s", name); + ret = volume ? PTR_ERR(volume) : -EOPNOTSUPP; + volume = NULL; + } + + sbi->volume = volume; + kfree(name); + return ret; +} + +static int erofs_fscache_init_domain(struct super_block *sb) +{ + int err; + struct erofs_domain *domain; + struct erofs_sb_info *sbi = EROFS_SB(sb); + + domain = kzalloc(sizeof(struct erofs_domain), GFP_KERNEL); + if (!domain) + return -ENOMEM; + + domain->domain_id = kstrdup(sbi->opt.domain_id, GFP_KERNEL); + if (!domain->domain_id) { + kfree(domain); + return -ENOMEM; + } + + err = erofs_fscache_register_volume(sb); + if (err) + goto out; + + domain->volume = sbi->volume; + refcount_set(&domain->ref, 1); + list_add(&domain->list, &erofs_domain_list); + sbi->domain = domain; + return 0; +out: + kfree(domain->domain_id); + kfree(domain); + return err; +} + +static int erofs_fscache_register_domain(struct super_block *sb) +{ + int err; + struct erofs_domain *domain; + struct erofs_sb_info *sbi = EROFS_SB(sb); + + mutex_lock(&erofs_domain_list_lock); + list_for_each_entry(domain, &erofs_domain_list, list) { + if (!strcmp(domain->domain_id, sbi->opt.domain_id)) { + sbi->domain = domain; + sbi->volume = domain->volume; + refcount_inc(&domain->ref); + mutex_unlock(&erofs_domain_list_lock); + return 0; + } + } + err = erofs_fscache_init_domain(sb); + mutex_unlock(&erofs_domain_list_lock); + return err; +} + struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, char *name, bool need_inode) { @@ -332,29 +431,19 @@ void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) int erofs_fscache_register_fs(struct super_block *sb) { - + int ret; struct erofs_sb_info *sbi = EROFS_SB(sb); - struct fscache_cookie *volume; struct erofs_fscache *fscache; - char *name; - - name = kasprintf(GFP_KERNEL, "erofs,%s", sbi->opt.fsid); - if (!name) - return -ENOMEM; - volume = fscache_acquire_cookie(erofs_fscache_netfs.primary_index, - &erofs_fscache_super_index_def, name, strlen(name), - NULL, 0, NULL, 0, true); - if (IS_ERR_OR_NULL(volume)) { - erofs_err(sb, "failed to register volume for %s", name); - kfree(name); - return volume ? PTR_ERR(volume) : -EOPNOTSUPP; - } - sbi->volume = volume; - kfree(name); + if (sbi->opt.domain_id) + ret = erofs_fscache_register_domain(sb); + else + ret = erofs_fscache_register_volume(sb); + if (ret) + return ret; + /* acquired domain/volume will be relinquished in kill_sb() on error */ fscache = erofs_fscache_register_cookie(sb, sbi->opt.fsid, true); - /* acquired volume will be relinquished in kill_sb() */ if (IS_ERR(fscache)) return PTR_ERR(fscache); @@ -367,7 +456,13 @@ void erofs_fscache_unregister_fs(struct super_block *sb) struct erofs_sb_info *sbi = EROFS_SB(sb); erofs_fscache_unregister_cookie(sbi->s_fscache); - fscache_relinquish_cookie(sbi->volume, NULL, false); + + if (sbi->domain) + erofs_fscache_domain_put(sbi->domain); + else + fscache_relinquish_cookie(sbi->volume, NULL, false); + sbi->s_fscache = NULL; sbi->volume = NULL; + sbi->domain = NULL; } diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index de945aba1674..8ece0e8a261b 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -66,6 +66,7 @@ struct erofs_mount_opts { #endif unsigned int mount_opt; char *fsid; + char *domain_id; }; struct erofs_dev_context { @@ -80,6 +81,13 @@ struct erofs_fs_context { struct erofs_dev_context *devs; }; +struct erofs_domain { + refcount_t ref; + struct list_head list; + struct fscache_cookie *volume; + char *domain_id; +}; + struct erofs_fscache { struct fscache_cookie *cookie; struct inode *inode; @@ -129,6 +137,7 @@ struct erofs_sb_info { /* fscache support */ struct fscache_cookie *volume; struct erofs_fscache *s_fscache; + struct erofs_domain *domain; }; #define EROFS_SB(sb) ((struct erofs_sb_info *)(sb)->s_fs_info) -- Gitee From 3754d4e30c386666c839536734a80945c0b341ea Mon Sep 17 00:00:00 2001 From: Jia Zhu Date: Sun, 18 Sep 2022 12:34:54 +0800 Subject: [PATCH 4/8] erofs: introduce a pseudo mnt to manage shared cookies ANBZ: #3234 commit a9849560c55e9e4ab9c53d073363dd6e19ec06ef upstream. Use a pseudo mnt to manage shared cookies. Signed-off-by: Jia Zhu Reviewed-by: Jingbo Xu Link: https://lore.kernel.org/r/20220918043456.147-5-zhujia.zj@bytedance.com Signed-off-by: Gao Xiang Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 13 +++++++++++++ fs/erofs/internal.h | 1 + fs/erofs/super.c | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index e6e68436a493..f99e2fd3a146 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -7,6 +7,7 @@ static DEFINE_MUTEX(erofs_domain_list_lock); static LIST_HEAD(erofs_domain_list); +static struct vfsmount *erofs_pseudo_mnt; struct fscache_netfs erofs_fscache_netfs = { .name = "erofs", @@ -279,6 +280,10 @@ static void erofs_fscache_domain_put(struct erofs_domain *domain) mutex_lock(&erofs_domain_list_lock); if (refcount_dec_and_test(&domain->ref)) { list_del(&domain->list); + if (list_empty(&erofs_domain_list)) { + kern_unmount(erofs_pseudo_mnt); + erofs_pseudo_mnt = NULL; + } mutex_unlock(&erofs_domain_list_lock); fscache_relinquish_cookie(domain->volume, NULL, false); kfree(domain->domain_id); @@ -335,6 +340,14 @@ static int erofs_fscache_init_domain(struct super_block *sb) if (err) goto out; + if (!erofs_pseudo_mnt) { + erofs_pseudo_mnt = kern_mount(&erofs_fs_type); + if (IS_ERR(erofs_pseudo_mnt)) { + err = PTR_ERR(erofs_pseudo_mnt); + goto out; + } + } + domain->volume = sbi->volume; refcount_set(&domain->ref, 1); list_add(&domain->list, &erofs_domain_list); diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 8ece0e8a261b..3a1acd1a1f22 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -363,6 +363,7 @@ static inline unsigned int erofs_inode_datalayout(unsigned int value) } extern const struct super_operations erofs_sops; +extern struct file_system_type erofs_fs_type; extern const struct address_space_operations erofs_raw_access_aops; extern const struct address_space_operations z_erofs_aops; diff --git a/fs/erofs/super.c b/fs/erofs/super.c index bdcd7452d882..150c6a7d6fdb 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -485,6 +485,13 @@ static int erofs_init_managed_cache(struct super_block *sb) static int erofs_init_managed_cache(struct super_block *sb) { return 0; } #endif +static int erofs_fc_fill_pseudo_super(struct super_block *sb, struct fs_context *fc) +{ + static const struct tree_descr empty_descr = {""}; + + return simple_fill_super(sb, EROFS_SUPER_MAGIC, &empty_descr); +} + static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) { struct inode *inode; @@ -575,6 +582,11 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) return 0; } +static int erofs_fc_anon_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, erofs_fc_fill_pseudo_super); +} + static int erofs_fc_get_tree(struct fs_context *fc) { struct erofs_fs_context *ctx = fc->fs_private; @@ -647,10 +659,21 @@ static const struct fs_context_operations erofs_context_ops = { .free = erofs_fc_free, }; +static const struct fs_context_operations erofs_anon_context_ops = { + .get_tree = erofs_fc_anon_get_tree, +}; + static int erofs_init_fs_context(struct fs_context *fc) { - struct erofs_fs_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + struct erofs_fs_context *ctx; + + /* pseudo mount for anon inodes */ + if (fc->sb_flags & SB_KERNMOUNT) { + fc->ops = &erofs_anon_context_ops; + return 0; + } + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ctx->devs = kzalloc(sizeof(struct erofs_dev_context), GFP_KERNEL); @@ -677,6 +700,12 @@ static void erofs_kill_sb(struct super_block *sb) WARN_ON(sb->s_magic != EROFS_SUPER_MAGIC); + /* pseudo mount for anon inodes */ + if (sb->s_flags & SB_KERNMOUNT) { + kill_anon_super(sb); + return; + } + if (erofs_is_fscache_mode(sb)) kill_anon_super(sb); else @@ -708,7 +737,7 @@ static void erofs_put_super(struct super_block *sb) sbi->s_fscache = NULL; } -static struct file_system_type erofs_fs_type = { +struct file_system_type erofs_fs_type = { .owner = THIS_MODULE, .name = "erofs", .init_fs_context = erofs_init_fs_context, -- Gitee From ec4b5e42c8e578a1acbbe71b0a5cdaaaa2b609aa Mon Sep 17 00:00:00 2001 From: Jia Zhu Date: Sun, 18 Sep 2022 19:01:50 +0800 Subject: [PATCH 5/8] erofs: Support sharing cookies in the same domain ANBZ: #3234 commit 7d41963759feb3cfa4c1164b8b9db5d1f055932d upstream. Several erofs filesystems can belong to one domain, and data blobs can be shared among these erofs filesystems of same domain. Users could specify domain_id mount option to create or join into a domain. Signed-off-by: Jia Zhu Reviewed-by: Jingbo Xu Link: https://lore.kernel.org/r/20220918110150.6338-1-zhujia.zj@bytedance.com Signed-off-by: Gao Xiang Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 100 +++++++++++++++++++++++++++++++++++++++++--- fs/erofs/internal.h | 3 ++ 2 files changed, 97 insertions(+), 6 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index f99e2fd3a146..217290ab8a8c 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -3,9 +3,11 @@ * Copyright (C) 2022, Bytedance Inc. All rights reserved. */ #include +#include #include "internal.h" static DEFINE_MUTEX(erofs_domain_list_lock); +static DEFINE_MUTEX(erofs_domain_cookies_lock); static LIST_HEAD(erofs_domain_list); static struct vfsmount *erofs_pseudo_mnt; @@ -380,8 +382,9 @@ static int erofs_fscache_register_domain(struct super_block *sb) return err; } -struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, - char *name, bool need_inode) +static +struct erofs_fscache *erofs_fscache_acquire_cookie(struct super_block *sb, + char *name, bool need_inode) { struct erofs_fscache *ctx; struct fscache_cookie *cookie; @@ -431,17 +434,102 @@ struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, return ERR_PTR(ret); } -void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) +static void erofs_fscache_relinquish_cookie(struct erofs_fscache *ctx) { - if (!ctx) - return; - //fscache_unuse_cookie(ctx->cookie, NULL, NULL); fscache_relinquish_cookie(ctx->cookie, NULL, false); iput(ctx->inode); + kfree(ctx->name); kfree(ctx); } +static +struct erofs_fscache *erofs_fscache_domain_init_cookie(struct super_block *sb, + char *name, bool need_inode) +{ + int err; + struct inode *inode; + struct erofs_fscache *ctx; + struct erofs_domain *domain = EROFS_SB(sb)->domain; + + ctx = erofs_fscache_acquire_cookie(sb, name, need_inode); + if (IS_ERR(ctx)) + return ctx; + + ctx->name = kstrdup(name, GFP_KERNEL); + if (!ctx->name) { + err = -ENOMEM; + goto out; + } + + inode = new_inode(erofs_pseudo_mnt->mnt_sb); + if (!inode) { + err = -ENOMEM; + goto out; + } + + ctx->domain = domain; + ctx->anon_inode = inode; + inode->i_private = ctx; + refcount_inc(&domain->ref); + return ctx; +out: + erofs_fscache_relinquish_cookie(ctx); + return ERR_PTR(err); +} + +static +struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, + char *name, bool need_inode) +{ + struct inode *inode; + struct erofs_fscache *ctx; + struct erofs_domain *domain = EROFS_SB(sb)->domain; + struct super_block *psb = erofs_pseudo_mnt->mnt_sb; + + mutex_lock(&erofs_domain_cookies_lock); + list_for_each_entry(inode, &psb->s_inodes, i_sb_list) { + ctx = inode->i_private; + if (!ctx || ctx->domain != domain || strcmp(ctx->name, name)) + continue; + igrab(inode); + mutex_unlock(&erofs_domain_cookies_lock); + return ctx; + } + ctx = erofs_fscache_domain_init_cookie(sb, name, need_inode); + mutex_unlock(&erofs_domain_cookies_lock); + return ctx; +} + +struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, + char *name, bool need_inode) +{ + if (EROFS_SB(sb)->opt.domain_id) + return erofs_domain_register_cookie(sb, name, need_inode); + return erofs_fscache_acquire_cookie(sb, name, need_inode); +} + +void erofs_fscache_unregister_cookie(struct erofs_fscache *ctx) +{ + bool drop; + struct erofs_domain *domain; + + if (!ctx) + return; + domain = ctx->domain; + if (domain) { + mutex_lock(&erofs_domain_cookies_lock); + drop = atomic_read(&ctx->anon_inode->i_count) == 1; + iput(ctx->anon_inode); + mutex_unlock(&erofs_domain_cookies_lock); + if (!drop) + return; + } + + erofs_fscache_relinquish_cookie(ctx); + erofs_fscache_domain_put(domain); +} + int erofs_fscache_register_fs(struct super_block *sb) { int ret; diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 3a1acd1a1f22..0dec91ee2016 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -91,6 +91,9 @@ struct erofs_domain { struct erofs_fscache { struct fscache_cookie *cookie; struct inode *inode; + struct inode *anon_inode; + struct erofs_domain *domain; + char *name; }; struct erofs_sb_info { -- Gitee From ec5136ad9a3bd16ce7571177c68b018a1c25e804 Mon Sep 17 00:00:00 2001 From: Jia Zhu Date: Sun, 18 Sep 2022 12:34:56 +0800 Subject: [PATCH 6/8] erofs: introduce 'domain_id' mount option ANBZ: #3234 commit 2ef164414123fcf574aff7a0be5f71f7e60a3fec upstream. Introduce 'domain_id' mount option to enable shared domain sementics. In which case, the related cookie is shared if two mountpoints in the same domain have the same data blob. Users could specify the name of domain by this mount option. Signed-off-by: Jia Zhu Reviewed-by: Jingbo Xu Link: https://lore.kernel.org/r/20220918043456.147-7-zhujia.zj@bytedance.com Signed-off-by: Gao Xiang Signed-off-by: Jingbo Xu --- fs/erofs/super.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 150c6a7d6fdb..7c5761761045 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -318,6 +318,7 @@ enum { Opt_cache_strategy, Opt_device, Opt_fsid, + Opt_domain_id, Opt_opt_creds, Opt_err }; @@ -336,6 +337,7 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = { erofs_param_cache_strategy), fsparam_string("device", Opt_device), fsparam_string("fsid", Opt_fsid), + fsparam_string("domain_id", Opt_domain_id), fsparam_string("opt_creds", Opt_opt_creds), {} }; @@ -408,6 +410,16 @@ static int erofs_fc_parse_param(struct fs_context *fc, #else errorfc(fc, "fsid option not supported"); return -EINVAL; +#endif + break; + case Opt_domain_id: +#ifdef CONFIG_EROFS_FS_ONDEMAND + kfree(ctx->opt.domain_id); + ctx->opt.domain_id = kstrdup(param->string, GFP_KERNEL); + if (!ctx->opt.domain_id) + return -ENOMEM; +#else + errorfc(fc, "domain_id option not supported"); #endif break; case Opt_opt_creds: @@ -511,6 +523,7 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_fs_info = sbi; sbi->opt = ctx->opt; ctx->opt.fsid = NULL; + ctx->opt.domain_id = NULL; sbi->devs = ctx->devs; ctx->devs = NULL; @@ -649,6 +662,7 @@ static void erofs_fc_free(struct fs_context *fc) erofs_free_dev_context(ctx->devs); kfree(ctx->opt.fsid); + kfree(ctx->opt.domain_id); kfree(ctx); } @@ -717,6 +731,7 @@ static void erofs_kill_sb(struct super_block *sb) erofs_free_dev_context(sbi->devs); erofs_fscache_unregister_fs(sb); kfree(sbi->opt.fsid); + kfree(sbi->opt.domain_id); kfree(sbi); sb->s_fs_info = NULL; } @@ -855,6 +870,8 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) #ifdef CONFIG_EROFS_FS_ONDEMAND if (sbi->opt.fsid) seq_printf(seq, ",fsid=%s", sbi->opt.fsid); + if (opt->domain_id) + seq_printf(seq, ",domain_id=%s", opt->domain_id); #endif if (test_opt(opt, OPT_CREDS)) seq_puts(seq, ",opt_creds=on"); -- Gitee From d13bedb58a4a82809de1c7ba8af50c6d32b5ee60 Mon Sep 17 00:00:00 2001 From: Dawei Li Date: Mon, 17 Oct 2022 09:55:53 +0800 Subject: [PATCH 7/8] erofs: protect s_inodes with s_inode_list_lock for fscache ANBZ: #3234 commit ce4b815686573bef82d5ee53bf6f509bf20904dc upstream. s_inodes is superblock-specific resource, which should be protected by sb's specific lock s_inode_list_lock. Link: https://lore.kernel.org/r/TYCP286MB23238380DE3B74874E8D78ABCA299@TYCP286MB2323.JPNP286.PROD.OUTLOOK.COM Fixes: 7d41963759fe ("erofs: Support sharing cookies in the same domain") Reviewed-by: Yue Hu Reviewed-by: Jia Zhu Reviewed-by: Jingbo Xu Signed-off-by: Dawei Li Signed-off-by: Gao Xiang Signed-off-by: Jingbo Xu --- fs/erofs/fscache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index 217290ab8a8c..a6302a06f1e4 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -488,14 +488,17 @@ struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, struct super_block *psb = erofs_pseudo_mnt->mnt_sb; mutex_lock(&erofs_domain_cookies_lock); + spin_lock(&psb->s_inode_list_lock); list_for_each_entry(inode, &psb->s_inodes, i_sb_list) { ctx = inode->i_private; if (!ctx || ctx->domain != domain || strcmp(ctx->name, name)) continue; igrab(inode); + spin_unlock(&psb->s_inode_list_lock); mutex_unlock(&erofs_domain_cookies_lock); return ctx; } + spin_unlock(&psb->s_inode_list_lock); ctx = erofs_fscache_domain_init_cookie(sb, name, need_inode); mutex_unlock(&erofs_domain_cookies_lock); return ctx; -- Gitee From 1edaf01099f0acb1953445fed5f196f299916780 Mon Sep 17 00:00:00 2001 From: Jingbo Xu Date: Fri, 21 Oct 2022 10:31:53 +0800 Subject: [PATCH 8/8] erofs: fix use-after-free of fsid and domain_id string ANBZ: #3234 commit 39bfcb8138f6dc3375f23b1e62ccfc7c0d83295d upstream. When erofs instance is remounted with fsid or domain_id mount option specified, the original fsid and domain_id string pointer in sbi->opt is directly overridden with the fsid and domain_id string in the new fs_context, without freeing the original fsid and domain_id string. What's worse, when the new fsid and domain_id string is transferred to sbi, they are not reset to NULL in fs_context, and thus they are freed when remount finishes, while sbi is still referring to these strings. Reconfiguration for fsid and domain_id seems unusual. Thus clarify this restriction explicitly and dump a warning when users are attempting to do this. Besides, to fix the use-after-free issue, move fsid and domain_id from erofs_mount_opts to outside. Fixes: c6be2bd0a5dd ("erofs: register fscache volume") Fixes: 8b7adf1dff3d ("erofs: introduce fscache-based domain") Signed-off-by: Jingbo Xu Reviewed-by: Jia Zhu Reviewed-by: Chao Yu Link: https://lore.kernel.org/r/20221021023153.1330-1-jefflexu@linux.alibaba.com Signed-off-by: Gao Xiang --- fs/erofs/fscache.c | 14 +++++++------- fs/erofs/internal.h | 6 ++++-- fs/erofs/super.c | 39 ++++++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/fs/erofs/fscache.c b/fs/erofs/fscache.c index a6302a06f1e4..5c7208418d44 100644 --- a/fs/erofs/fscache.c +++ b/fs/erofs/fscache.c @@ -298,13 +298,13 @@ static void erofs_fscache_domain_put(struct erofs_domain *domain) static int erofs_fscache_register_volume(struct super_block *sb) { struct erofs_sb_info *sbi = EROFS_SB(sb); - char *domain_id = sbi->opt.domain_id; + char *domain_id = sbi->domain_id; struct fscache_cookie *volume; char *name; int ret = 0; name = kasprintf(GFP_KERNEL, "erofs,%s", - domain_id ? domain_id : sbi->opt.fsid); + domain_id ? domain_id : sbi->fsid); if (!name) return -ENOMEM; @@ -332,7 +332,7 @@ static int erofs_fscache_init_domain(struct super_block *sb) if (!domain) return -ENOMEM; - domain->domain_id = kstrdup(sbi->opt.domain_id, GFP_KERNEL); + domain->domain_id = kstrdup(sbi->domain_id, GFP_KERNEL); if (!domain->domain_id) { kfree(domain); return -ENOMEM; @@ -369,7 +369,7 @@ static int erofs_fscache_register_domain(struct super_block *sb) mutex_lock(&erofs_domain_list_lock); list_for_each_entry(domain, &erofs_domain_list, list) { - if (!strcmp(domain->domain_id, sbi->opt.domain_id)) { + if (!strcmp(domain->domain_id, sbi->domain_id)) { sbi->domain = domain; sbi->volume = domain->volume; refcount_inc(&domain->ref); @@ -507,7 +507,7 @@ struct erofs_fscache *erofs_domain_register_cookie(struct super_block *sb, struct erofs_fscache *erofs_fscache_register_cookie(struct super_block *sb, char *name, bool need_inode) { - if (EROFS_SB(sb)->opt.domain_id) + if (EROFS_SB(sb)->domain_id) return erofs_domain_register_cookie(sb, name, need_inode); return erofs_fscache_acquire_cookie(sb, name, need_inode); } @@ -539,7 +539,7 @@ int erofs_fscache_register_fs(struct super_block *sb) struct erofs_sb_info *sbi = EROFS_SB(sb); struct erofs_fscache *fscache; - if (sbi->opt.domain_id) + if (sbi->domain_id) ret = erofs_fscache_register_domain(sb); else ret = erofs_fscache_register_volume(sb); @@ -547,7 +547,7 @@ int erofs_fscache_register_fs(struct super_block *sb) return ret; /* acquired domain/volume will be relinquished in kill_sb() on error */ - fscache = erofs_fscache_register_cookie(sb, sbi->opt.fsid, true); + fscache = erofs_fscache_register_cookie(sb, sbi->fsid, true); if (IS_ERR(fscache)) return PTR_ERR(fscache); diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h index 0dec91ee2016..dfe170a2e7d8 100644 --- a/fs/erofs/internal.h +++ b/fs/erofs/internal.h @@ -65,8 +65,6 @@ struct erofs_mount_opts { unsigned int max_sync_decompress_pages; #endif unsigned int mount_opt; - char *fsid; - char *domain_id; }; struct erofs_dev_context { @@ -79,6 +77,8 @@ struct erofs_dev_context { struct erofs_fs_context { struct erofs_mount_opts opt; struct erofs_dev_context *devs; + char *fsid; + char *domain_id; }; struct erofs_domain { @@ -141,6 +141,8 @@ struct erofs_sb_info { struct fscache_cookie *volume; struct erofs_fscache *s_fscache; struct erofs_domain *domain; + char *fsid; + char *domain_id; }; #define EROFS_SB(sb) ((struct erofs_sb_info *)(sb)->s_fs_info) diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 7c5761761045..7057a20a7ab5 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -403,9 +403,9 @@ static int erofs_fc_parse_param(struct fs_context *fc, break; case Opt_fsid: #ifdef CONFIG_EROFS_FS_ONDEMAND - kfree(ctx->opt.fsid); - ctx->opt.fsid = kstrdup(param->string, GFP_KERNEL); - if (!ctx->opt.fsid) + kfree(ctx->fsid); + ctx->fsid = kstrdup(param->string, GFP_KERNEL); + if (!ctx->fsid) return -ENOMEM; #else errorfc(fc, "fsid option not supported"); @@ -414,9 +414,9 @@ static int erofs_fc_parse_param(struct fs_context *fc, break; case Opt_domain_id: #ifdef CONFIG_EROFS_FS_ONDEMAND - kfree(ctx->opt.domain_id); - ctx->opt.domain_id = kstrdup(param->string, GFP_KERNEL); - if (!ctx->opt.domain_id) + kfree(ctx->domain_id); + ctx->domain_id = kstrdup(param->string, GFP_KERNEL); + if (!ctx->domain_id) return -ENOMEM; #else errorfc(fc, "domain_id option not supported"); @@ -522,10 +522,12 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_fs_info = sbi; sbi->opt = ctx->opt; - ctx->opt.fsid = NULL; - ctx->opt.domain_id = NULL; sbi->devs = ctx->devs; ctx->devs = NULL; + sbi->fsid = ctx->fsid; + ctx->fsid = NULL; + sbi->domain_id = ctx->domain_id; + ctx->domain_id = NULL; if (erofs_is_fscache_mode(sb)) { sb->s_blocksize = EROFS_BLKSIZ; @@ -604,7 +606,7 @@ static int erofs_fc_get_tree(struct fs_context *fc) { struct erofs_fs_context *ctx = fc->fs_private; - if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && ctx->opt.fsid) + if (IS_ENABLED(CONFIG_EROFS_FS_ONDEMAND) && ctx->fsid) return get_tree_nodev(fc, erofs_fc_fill_super); return get_tree_bdev(fc, erofs_fc_fill_super); @@ -618,6 +620,9 @@ static int erofs_fc_reconfigure(struct fs_context *fc) DBG_BUGON(!sb_rdonly(sb)); + if (ctx->fsid || ctx->domain_id) + erofs_info(sb, "ignoring reconfiguration for fsid|domain_id."); + if (test_opt(&ctx->opt, POSIX_ACL)) fc->sb_flags |= SB_POSIXACL; else @@ -661,8 +666,8 @@ static void erofs_fc_free(struct fs_context *fc) struct erofs_fs_context *ctx = fc->fs_private; erofs_free_dev_context(ctx->devs); - kfree(ctx->opt.fsid); - kfree(ctx->opt.domain_id); + kfree(ctx->fsid); + kfree(ctx->domain_id); kfree(ctx); } @@ -730,8 +735,8 @@ static void erofs_kill_sb(struct super_block *sb) return; erofs_free_dev_context(sbi->devs); erofs_fscache_unregister_fs(sb); - kfree(sbi->opt.fsid); - kfree(sbi->opt.domain_id); + kfree(sbi->fsid); + kfree(sbi->domain_id); kfree(sbi); sb->s_fs_info = NULL; } @@ -868,10 +873,10 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",cache_strategy=readaround"); #endif #ifdef CONFIG_EROFS_FS_ONDEMAND - if (sbi->opt.fsid) - seq_printf(seq, ",fsid=%s", sbi->opt.fsid); - if (opt->domain_id) - seq_printf(seq, ",domain_id=%s", opt->domain_id); + if (sbi->fsid) + seq_printf(seq, ",fsid=%s", sbi->fsid); + if (sbi->domain_id) + seq_printf(seq, ",domain_id=%s", sbi->domain_id); #endif if (test_opt(opt, OPT_CREDS)) seq_puts(seq, ",opt_creds=on"); -- Gitee