Commit 8b7adf1d authored by Jia Zhu's avatar Jia Zhu Committed by Gao Xiang
Browse files

erofs: introduce fscache-based domain



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: default avatarJia Zhu <zhujia.zj@bytedance.com>
Reviewed-by: default avatarJingbo Xu <jefflexu@linux.alibaba.com>
Link: https://lore.kernel.org/r/20220918043456.147-4-zhujia.zj@bytedance.com


Signed-off-by: default avatarGao Xiang <hsiangkao@linux.alibaba.com>
parent e1de2da0
Loading
Loading
Loading
Loading
+112 −17
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2022, Alibaba Cloud
 * Copyright (C) 2022, Bytedance Inc. All rights reserved.
 */
#include <linux/fscache.h>
#include "internal.h"

static DEFINE_MUTEX(erofs_domain_list_lock);
static LIST_HEAD(erofs_domain_list);

static struct netfs_io_request *erofs_fscache_alloc_request(struct address_space *mapping,
					     loff_t start, size_t len)
{
@@ -421,6 +425,99 @@ const struct address_space_operations erofs_fscache_access_aops = {
	.readahead = erofs_fscache_readahead,
};

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_volume(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_volume *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_volume(name, NULL, NULL, 0);
	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)
{
@@ -484,27 +581,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_volume *volume;
	struct erofs_fscache *fscache;
	char *name;

	name = kasprintf(GFP_KERNEL, "erofs,%s", sbi->opt.fsid);
	if (!name)
		return -ENOMEM;

	volume = fscache_acquire_volume(name, NULL, NULL, 0);
	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);

@@ -517,7 +606,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);

	if (sbi->domain)
		erofs_fscache_domain_put(sbi->domain);
	else
		fscache_relinquish_volume(sbi->volume, NULL, false);

	sbi->s_fscache = NULL;
	sbi->volume = NULL;
	sbi->domain = NULL;
}
+9 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ struct erofs_mount_opts {
#endif
	unsigned int mount_opt;
	char *fsid;
	char *domain_id;
};

struct erofs_dev_context {
@@ -98,6 +99,13 @@ struct erofs_sb_lz4_info {
	u16 max_pclusterblks;
};

struct erofs_domain {
	refcount_t ref;
	struct list_head list;
	struct fscache_volume *volume;
	char *domain_id;
};

struct erofs_fscache {
	struct fscache_cookie *cookie;
	struct inode *inode;
@@ -157,6 +165,7 @@ struct erofs_sb_info {
	/* fscache support */
	struct fscache_volume *volume;
	struct erofs_fscache *s_fscache;
	struct erofs_domain *domain;
};

#define EROFS_SB(sb) ((struct erofs_sb_info *)(sb)->s_fs_info)