162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/hyperhold/hp_core.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci #define pr_fmt(fmt) "[HYPERHOLD]" fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/blkdev.h>
1262306a36Sopenharmony_ci#include <linux/sysctl.h>
1362306a36Sopenharmony_ci#include <linux/version.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "hyperhold.h"
1662306a36Sopenharmony_ci#include "hp_device.h"
1762306a36Sopenharmony_ci#include "hp_space.h"
1862306a36Sopenharmony_ci#include "hp_iotab.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define HP_DFLT_DEVICE "/dev/by-name/hyperhold"
2162306a36Sopenharmony_ci#define HP_DFLT_EXT_SIZE (1 << 15)
2262306a36Sopenharmony_ci#define HP_DEV_NAME_LEN 256
2362306a36Sopenharmony_ci#define HP_STATE_LEN 10
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define CHECK(cond, ...) ((cond) || (pr_err(__VA_ARGS__), false))
2662306a36Sopenharmony_ci#define CHECK_BOUND(var, min, max) \
2762306a36Sopenharmony_ci	CHECK((var) >= (min) && (var) <= (max), \
2862306a36Sopenharmony_ci		"%s %u out of bounds %u ~ %u!\n", #var, (var), (min), (max))
2962306a36Sopenharmony_ci#define CHECK_INITED CHECK(hyperhold.inited, "hyperhold is not enable!\n")
3062306a36Sopenharmony_ci#define CHECK_ENABLE (CHECK_INITED && CHECK(hyperhold.enable, "hyperhold is readonly!\n"))
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct hyperhold {
3362306a36Sopenharmony_ci	bool enable;
3462306a36Sopenharmony_ci	bool inited;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	char device_name[HP_DEV_NAME_LEN];
3762306a36Sopenharmony_ci	u32 extent_size;
3862306a36Sopenharmony_ci	u32 enable_soft_crypt;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	struct hp_device dev;
4162306a36Sopenharmony_ci	struct hp_space spc;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	struct workqueue_struct *read_wq;
4462306a36Sopenharmony_ci	struct workqueue_struct *write_wq;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	struct mutex init_lock;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct hyperhold hyperhold;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciatomic64_t mem_used = ATOMIC64_INIT(0);
5262306a36Sopenharmony_ci#ifdef CONFIG_HYPERHOLD_DEBUG
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * return the memory overhead of hyperhold module
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ciu64 hyperhold_memory_used(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return atomic64_read(&mem_used) + hpio_memory() + space_memory();
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci#endif
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_civoid hyperhold_disable(bool force)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (!CHECK_INITED)
6562306a36Sopenharmony_ci		return;
6662306a36Sopenharmony_ci	if (!force && !CHECK_ENABLE)
6762306a36Sopenharmony_ci		return;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	mutex_lock(&hyperhold.init_lock);
7062306a36Sopenharmony_ci	hyperhold.enable = false;
7162306a36Sopenharmony_ci	if (!wait_for_space_empty(&hyperhold.spc, force))
7262306a36Sopenharmony_ci		goto out;
7362306a36Sopenharmony_ci	hyperhold.inited = false;
7462306a36Sopenharmony_ci	wait_for_iotab_empty();
7562306a36Sopenharmony_ci	destroy_workqueue(hyperhold.read_wq);
7662306a36Sopenharmony_ci	destroy_workqueue(hyperhold.write_wq);
7762306a36Sopenharmony_ci	deinit_space(&hyperhold.spc);
7862306a36Sopenharmony_ci	crypto_deinit(&hyperhold.dev);
7962306a36Sopenharmony_ci	unbind_bdev(&hyperhold.dev);
8062306a36Sopenharmony_ciout:
8162306a36Sopenharmony_ci	if (hyperhold.inited)
8262306a36Sopenharmony_ci		pr_info("hyperhold is disabled, read only.\n");
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		pr_info("hyperhold is totally disabled!\n");
8562306a36Sopenharmony_ci	mutex_unlock(&hyperhold.init_lock);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_disable);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_civoid hyperhold_enable(void)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	bool enable = true;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (hyperhold.inited)
9462306a36Sopenharmony_ci		goto out;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	mutex_lock(&hyperhold.init_lock);
9762306a36Sopenharmony_ci	if (hyperhold.inited)
9862306a36Sopenharmony_ci		goto unlock;
9962306a36Sopenharmony_ci	if (!bind_bdev(&hyperhold.dev, hyperhold.device_name))
10062306a36Sopenharmony_ci		goto err1;
10162306a36Sopenharmony_ci	if (!crypto_init(&hyperhold.dev, hyperhold.enable_soft_crypt))
10262306a36Sopenharmony_ci		goto err2;
10362306a36Sopenharmony_ci	if (!init_space(&hyperhold.spc, hyperhold.dev.dev_size, hyperhold.extent_size))
10462306a36Sopenharmony_ci		goto err3;
10562306a36Sopenharmony_ci	hyperhold.read_wq = alloc_workqueue("hyperhold_read", WQ_HIGHPRI | WQ_UNBOUND, 0);
10662306a36Sopenharmony_ci	if (!hyperhold.read_wq)
10762306a36Sopenharmony_ci		goto err4;
10862306a36Sopenharmony_ci	hyperhold.write_wq = alloc_workqueue("hyperhold_write", 0, 0);
10962306a36Sopenharmony_ci	if (!hyperhold.write_wq)
11062306a36Sopenharmony_ci		goto err5;
11162306a36Sopenharmony_ci	hyperhold.inited = true;
11262306a36Sopenharmony_ci	goto unlock;
11362306a36Sopenharmony_cierr5:
11462306a36Sopenharmony_ci	destroy_workqueue(hyperhold.read_wq);
11562306a36Sopenharmony_cierr4:
11662306a36Sopenharmony_ci	deinit_space(&hyperhold.spc);
11762306a36Sopenharmony_cierr3:
11862306a36Sopenharmony_ci	crypto_deinit(&hyperhold.dev);
11962306a36Sopenharmony_cierr2:
12062306a36Sopenharmony_ci	unbind_bdev(&hyperhold.dev);
12162306a36Sopenharmony_cierr1:
12262306a36Sopenharmony_ci	enable = false;
12362306a36Sopenharmony_ciunlock:
12462306a36Sopenharmony_ci	mutex_unlock(&hyperhold.init_lock);
12562306a36Sopenharmony_ciout:
12662306a36Sopenharmony_ci	if (enable) {
12762306a36Sopenharmony_ci		hyperhold.enable = true;
12862306a36Sopenharmony_ci		pr_info("hyperhold is enabled.\n");
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		hyperhold.enable = false;
13162306a36Sopenharmony_ci		pr_err("hyperhold enable failed!\n");
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_enable);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic int enable_sysctl_handler(struct ctl_table *table, int write,
13762306a36Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	const struct cred *cred = current_cred();
14062306a36Sopenharmony_ci	char *filter_buf;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	filter_buf = strstrip((char *)buffer);
14362306a36Sopenharmony_ci	if (write) {
14462306a36Sopenharmony_ci		if (!uid_eq(cred->euid, GLOBAL_MEMMGR_UID) &&
14562306a36Sopenharmony_ci			!uid_eq(cred->euid, GLOBAL_ROOT_UID)) {
14662306a36Sopenharmony_ci			pr_err("no permission to enable/disable eswap!\n");
14762306a36Sopenharmony_ci			return 0;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci		if (!strcmp(filter_buf, "enable"))
15062306a36Sopenharmony_ci			hyperhold_enable();
15162306a36Sopenharmony_ci		else if (!strcmp(filter_buf, "disable"))
15262306a36Sopenharmony_ci			hyperhold_disable(false);
15362306a36Sopenharmony_ci		else if (!strcmp(filter_buf, "force_disable"))
15462306a36Sopenharmony_ci			hyperhold_disable(true);
15562306a36Sopenharmony_ci	} else {
15662306a36Sopenharmony_ci		if (*lenp < HP_STATE_LEN || *ppos) {
15762306a36Sopenharmony_ci			*lenp = 0;
15862306a36Sopenharmony_ci			return 0;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		if (hyperhold.enable)
16162306a36Sopenharmony_ci			strcpy(buffer, "enable\n");
16262306a36Sopenharmony_ci		else if (hyperhold.inited)
16362306a36Sopenharmony_ci			strcpy(buffer, "readonly\n");
16462306a36Sopenharmony_ci		else
16562306a36Sopenharmony_ci			strcpy(buffer, "disable\n");
16662306a36Sopenharmony_ci		*lenp = strlen(buffer);
16762306a36Sopenharmony_ci		*ppos += *lenp;
16862306a36Sopenharmony_ci#ifdef CONFIG_HYPERHOLD_DEBUG
16962306a36Sopenharmony_ci		pr_info("hyperhold memory overhead = %llu.\n", hyperhold_memory_used());
17062306a36Sopenharmony_ci#endif
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int device_sysctl_handler(struct ctl_table *table, int write,
17662306a36Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int ret;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	mutex_lock(&hyperhold.init_lock);
18162306a36Sopenharmony_ci	if (write && hyperhold.inited) {
18262306a36Sopenharmony_ci		pr_err("hyperhold device is busy!\n");
18362306a36Sopenharmony_ci		ret = -EBUSY;
18462306a36Sopenharmony_ci		goto unlock;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	ret = proc_dostring(table, write, buffer, lenp, ppos);
18762306a36Sopenharmony_ci	if (write && !ret) {
18862306a36Sopenharmony_ci		hyperhold.enable_soft_crypt = 1;
18962306a36Sopenharmony_ci		pr_info("device changed, default enable soft crypt.\n");
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ciunlock:
19262306a36Sopenharmony_ci	mutex_unlock(&hyperhold.init_lock);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return ret;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int extent_sysctl_handler(struct ctl_table *table, int write,
19862306a36Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	mutex_lock(&hyperhold.init_lock);
20362306a36Sopenharmony_ci	if (write && hyperhold.inited) {
20462306a36Sopenharmony_ci		pr_err("hyperhold device is busy!\n");
20562306a36Sopenharmony_ci		ret = -EBUSY;
20662306a36Sopenharmony_ci		goto unlock;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	ret = proc_douintvec(table, write, buffer, lenp, ppos);
20962306a36Sopenharmony_ciunlock:
21062306a36Sopenharmony_ci	mutex_unlock(&hyperhold.init_lock);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int crypto_sysctl_handler(struct ctl_table *table, int write,
21662306a36Sopenharmony_ci				 void *buffer, size_t *lenp, loff_t *ppos)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	int ret;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	mutex_lock(&hyperhold.init_lock);
22162306a36Sopenharmony_ci	if (write && hyperhold.inited) {
22262306a36Sopenharmony_ci		pr_err("hyperhold device is busy!\n");
22362306a36Sopenharmony_ci		ret = -EBUSY;
22462306a36Sopenharmony_ci		goto unlock;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	ret = proc_douintvec_minmax(table, write, buffer, lenp, ppos);
22762306a36Sopenharmony_ciunlock:
22862306a36Sopenharmony_ci	mutex_unlock(&hyperhold.init_lock);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return ret;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic struct ctl_table_header *hp_sysctl_header;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
23662306a36Sopenharmony_cistatic struct ctl_table hp_sys_table[] = {
23762306a36Sopenharmony_ci	{
23862306a36Sopenharmony_ci		.procname = "enable",
23962306a36Sopenharmony_ci		.mode = 0666,
24062306a36Sopenharmony_ci		.proc_handler = enable_sysctl_handler,
24162306a36Sopenharmony_ci	},
24262306a36Sopenharmony_ci	{
24362306a36Sopenharmony_ci		.procname = "device",
24462306a36Sopenharmony_ci		.data = &hyperhold.device_name,
24562306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.device_name),
24662306a36Sopenharmony_ci		.mode = 0644,
24762306a36Sopenharmony_ci		.proc_handler = device_sysctl_handler,
24862306a36Sopenharmony_ci	},
24962306a36Sopenharmony_ci	{
25062306a36Sopenharmony_ci		.procname = "extent_size",
25162306a36Sopenharmony_ci		.data = &hyperhold.extent_size,
25262306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.extent_size),
25362306a36Sopenharmony_ci		.mode = 0644,
25462306a36Sopenharmony_ci		.proc_handler = extent_sysctl_handler,
25562306a36Sopenharmony_ci	},
25662306a36Sopenharmony_ci	{
25762306a36Sopenharmony_ci		.procname = "soft_crypt",
25862306a36Sopenharmony_ci		.data = &hyperhold.enable_soft_crypt,
25962306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.enable_soft_crypt),
26062306a36Sopenharmony_ci		.mode = 0644,
26162306a36Sopenharmony_ci		.proc_handler = crypto_sysctl_handler,
26262306a36Sopenharmony_ci		.extra1 = SYSCTL_ZERO,
26362306a36Sopenharmony_ci		.extra2 = SYSCTL_ONE,
26462306a36Sopenharmony_ci	},
26562306a36Sopenharmony_ci	{}
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci#else
26862306a36Sopenharmony_cistatic struct ctl_table hp_table[] = {
26962306a36Sopenharmony_ci	{
27062306a36Sopenharmony_ci		.procname = "enable",
27162306a36Sopenharmony_ci		.mode = 0666,
27262306a36Sopenharmony_ci		.proc_handler = enable_sysctl_handler,
27362306a36Sopenharmony_ci	},
27462306a36Sopenharmony_ci	{
27562306a36Sopenharmony_ci		.procname = "device",
27662306a36Sopenharmony_ci		.data = &hyperhold.device_name,
27762306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.device_name),
27862306a36Sopenharmony_ci		.mode = 0644,
27962306a36Sopenharmony_ci		.proc_handler = device_sysctl_handler,
28062306a36Sopenharmony_ci	},
28162306a36Sopenharmony_ci	{
28262306a36Sopenharmony_ci		.procname = "extent_size",
28362306a36Sopenharmony_ci		.data = &hyperhold.extent_size,
28462306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.extent_size),
28562306a36Sopenharmony_ci		.mode = 0644,
28662306a36Sopenharmony_ci		.proc_handler = extent_sysctl_handler,
28762306a36Sopenharmony_ci	},
28862306a36Sopenharmony_ci	{
28962306a36Sopenharmony_ci		.procname = "soft_crypt",
29062306a36Sopenharmony_ci		.data = &hyperhold.enable_soft_crypt,
29162306a36Sopenharmony_ci		.maxlen = sizeof(hyperhold.enable_soft_crypt),
29262306a36Sopenharmony_ci		.mode = 0644,
29362306a36Sopenharmony_ci		.proc_handler = crypto_sysctl_handler,
29462306a36Sopenharmony_ci		.extra1 = SYSCTL_ZERO,
29562306a36Sopenharmony_ci		.extra2 = SYSCTL_ONE,
29662306a36Sopenharmony_ci	},
29762306a36Sopenharmony_ci	{}
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_cistatic struct ctl_table hp_kernel_table[] = {
30062306a36Sopenharmony_ci	{
30162306a36Sopenharmony_ci		.procname = "hyperhold",
30262306a36Sopenharmony_ci		.mode = 0555,
30362306a36Sopenharmony_ci		.child = hp_table,
30462306a36Sopenharmony_ci	},
30562306a36Sopenharmony_ci	{}
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_cistatic struct ctl_table hp_sys_table[] = {
30862306a36Sopenharmony_ci	{
30962306a36Sopenharmony_ci		.procname = "kernel",
31062306a36Sopenharmony_ci		.mode = 0555,
31162306a36Sopenharmony_ci		.child = hp_kernel_table,
31262306a36Sopenharmony_ci	},
31362306a36Sopenharmony_ci	{}
31462306a36Sopenharmony_ci};
31562306a36Sopenharmony_ci#endif
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cibool is_hyperhold_enable(void)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	return hyperhold.enable;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int __init hyperhold_init(void)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	strcpy(hyperhold.device_name, HP_DFLT_DEVICE);
32562306a36Sopenharmony_ci	hyperhold.extent_size = HP_DFLT_EXT_SIZE;
32662306a36Sopenharmony_ci	hyperhold.enable_soft_crypt = 1;
32762306a36Sopenharmony_ci	mutex_init(&hyperhold.init_lock);
32862306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
32962306a36Sopenharmony_ci	hp_sysctl_header = register_sysctl("kernel/hyperhold", hp_sys_table);
33062306a36Sopenharmony_ci#else
33162306a36Sopenharmony_ci	hp_sysctl_header = register_sysctl_table(hp_sys_table);
33262306a36Sopenharmony_ci#endif
33362306a36Sopenharmony_ci	if (!hp_sysctl_header) {
33462306a36Sopenharmony_ci		pr_err("register hyperhold sysctl table failed!\n");
33562306a36Sopenharmony_ci		return -EINVAL;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void __exit hyperhold_exit(void)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	unregister_sysctl_table(hp_sysctl_header);
34462306a36Sopenharmony_ci	hyperhold_disable(true);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic struct hp_space *space_of(u32 eid)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	return &hyperhold.spc;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/* replace this func for multi devices */
35362306a36Sopenharmony_cistatic struct hp_device *device_of(u32 eid)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	return &hyperhold.dev;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/* replace this func for multi devices */
35962306a36Sopenharmony_ciu32 hyperhold_nr_extent(void)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	if (!CHECK_INITED)
36262306a36Sopenharmony_ci		return 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return hyperhold.spc.nr_ext;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_nr_extent);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciu32 hyperhold_extent_size(u32 eid)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct hp_space *spc = NULL;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!CHECK_INITED)
37362306a36Sopenharmony_ci		return 0;
37462306a36Sopenharmony_ci	spc = space_of(eid);
37562306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u!\n", eid))
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return spc->ext_size;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_extent_size);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/* replace this func for multi devices */
38362306a36Sopenharmony_cilong hyperhold_address(u32 eid, u32 offset)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct hp_space *spc = NULL;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (!CHECK_INITED)
38862306a36Sopenharmony_ci		return -EINVAL;
38962306a36Sopenharmony_ci	spc = space_of(eid);
39062306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u!\n", eid))
39162306a36Sopenharmony_ci		return -EINVAL;
39262306a36Sopenharmony_ci	if (!CHECK_BOUND(offset, 0, spc->ext_size - 1))
39362306a36Sopenharmony_ci		return -EINVAL;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return (u64)eid * spc->ext_size + offset;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_address);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/* replace this func for multi devices */
40062306a36Sopenharmony_ciint hyperhold_addr_extent(u64 addr)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct hp_space *spc = NULL;
40362306a36Sopenharmony_ci	u32 eid;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!CHECK_INITED)
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci	eid = div_u64(addr, hyperhold.spc.ext_size);
40862306a36Sopenharmony_ci	spc = space_of(eid);
40962306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u!\n", eid))
41062306a36Sopenharmony_ci		return -EINVAL;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return eid;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_addr_extent);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/* replace this func for multi devices */
41762306a36Sopenharmony_ciint hyperhold_addr_offset(u64 addr)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	if (!CHECK_INITED)
42062306a36Sopenharmony_ci		return -EINVAL;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return do_div(addr, hyperhold.spc.ext_size);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_addr_offset);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/* replace this func for multi devices */
42762306a36Sopenharmony_ciint hyperhold_alloc_extent(void)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	if (!CHECK_ENABLE)
43062306a36Sopenharmony_ci		return -EINVAL;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return alloc_eid(&hyperhold.spc);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_alloc_extent);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_civoid hyperhold_free_extent(u32 eid)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct hp_space *spc = NULL;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!CHECK_INITED)
44162306a36Sopenharmony_ci		return;
44262306a36Sopenharmony_ci	spc = space_of(eid);
44362306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u!\n", eid))
44462306a36Sopenharmony_ci		return;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	free_eid(spc, eid);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_free_extent);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_civoid hyperhold_should_free_extent(u32 eid)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct hpio *hpio = NULL;
45362306a36Sopenharmony_ci	struct hp_space *spc = NULL;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!CHECK_INITED)
45662306a36Sopenharmony_ci		return;
45762306a36Sopenharmony_ci	spc = space_of(eid);
45862306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u", eid))
45962306a36Sopenharmony_ci		return;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	hpio = hpio_get(eid);
46262306a36Sopenharmony_ci	if (!hpio) {
46362306a36Sopenharmony_ci		free_eid(spc, eid);
46462306a36Sopenharmony_ci		return;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	hpio->free_extent = hyperhold_free_extent;
46762306a36Sopenharmony_ci	hpio_put(hpio);
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_should_free_extent);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci * alloc hpio struct for r/w extent at @eid, will fill hpio with new alloced
47362306a36Sopenharmony_ci * pages if @new_page. @return NULL on fail.
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistruct hpio *hyperhold_io_alloc(u32 eid, gfp_t gfp, unsigned int op, bool new_page)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct hpio *hpio = NULL;
47862306a36Sopenharmony_ci	struct hp_space *spc;
47962306a36Sopenharmony_ci	u32 nr_page;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!CHECK_ENABLE)
48262306a36Sopenharmony_ci		return NULL;
48362306a36Sopenharmony_ci	spc = space_of(eid);
48462306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid  %u!\n", eid))
48562306a36Sopenharmony_ci		return NULL;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	nr_page = spc->ext_size / PAGE_SIZE;
48862306a36Sopenharmony_ci	hpio = hpio_alloc(nr_page, gfp, op, new_page);
48962306a36Sopenharmony_ci	if (!hpio)
49062306a36Sopenharmony_ci		goto err;
49162306a36Sopenharmony_ci	hpio->eid = eid;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return hpio;
49462306a36Sopenharmony_cierr:
49562306a36Sopenharmony_ci	hpio_free(hpio);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return NULL;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_alloc);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_civoid hyperhold_io_free(struct hpio *hpio)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	if (!CHECK_INITED)
50462306a36Sopenharmony_ci		return;
50562306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
50662306a36Sopenharmony_ci		return;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	hpio_free(hpio);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_free);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/*
51362306a36Sopenharmony_ci * find exist read hpio of the extent @eid in iotab and inc its refcnt,
51462306a36Sopenharmony_ci * alloc a new hpio and insert it into iotab if there is no hpio for @eid
51562306a36Sopenharmony_ci */
51662306a36Sopenharmony_cistruct hpio *hyperhold_io_get(u32 eid, gfp_t gfp, unsigned int op)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct hp_space *spc = NULL;
51962306a36Sopenharmony_ci	u32 nr_page;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (!CHECK_INITED)
52262306a36Sopenharmony_ci		return NULL;
52362306a36Sopenharmony_ci	spc = space_of(eid);
52462306a36Sopenharmony_ci	if (!CHECK(spc, "invalid eid %u", eid))
52562306a36Sopenharmony_ci		return NULL;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	nr_page = spc->ext_size / PAGE_SIZE;
52862306a36Sopenharmony_ci	return hpio_get_alloc(eid, nr_page, gfp, op);
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_get);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cibool hyperhold_io_put(struct hpio *hpio)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	if (!CHECK_INITED)
53562306a36Sopenharmony_ci		return false;
53662306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
53762306a36Sopenharmony_ci		return false;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return hpio_put(hpio);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_put);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/*
54462306a36Sopenharmony_ci * notify all threads waiting for this hpio
54562306a36Sopenharmony_ci */
54662306a36Sopenharmony_civoid hyperhold_io_complete(struct hpio *hpio)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	if (!CHECK_INITED)
54962306a36Sopenharmony_ci		return;
55062306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
55162306a36Sopenharmony_ci		return;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	hpio_complete(hpio);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_complete);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_civoid hyperhold_io_wait(struct hpio *hpio)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	if (!CHECK_INITED)
56062306a36Sopenharmony_ci		return;
56162306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
56262306a36Sopenharmony_ci		return;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	hpio_wait(hpio);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_wait);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cibool hyperhold_io_success(struct hpio *hpio)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	if (!CHECK_INITED)
57162306a36Sopenharmony_ci		return false;
57262306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
57362306a36Sopenharmony_ci		return false;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return hpio_get_state(hpio) == HPIO_DONE;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_success);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ciint hyperhold_io_extent(struct hpio *hpio)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	if (!CHECK_INITED)
58262306a36Sopenharmony_ci		return -EINVAL;
58362306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
58462306a36Sopenharmony_ci		return -EINVAL;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return hpio->eid;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_extent);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ciint hyperhold_io_operate(struct hpio *hpio)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	if (!CHECK_INITED)
59362306a36Sopenharmony_ci		return -EINVAL;
59462306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
59562306a36Sopenharmony_ci		return -EINVAL;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return hpio->op;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_operate);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistruct page *hyperhold_io_page(struct hpio *hpio, u32 index)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	if (!CHECK_INITED)
60462306a36Sopenharmony_ci		return NULL;
60562306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
60662306a36Sopenharmony_ci		return NULL;
60762306a36Sopenharmony_ci	if (!CHECK_BOUND(index, 0, hpio->nr_page - 1))
60862306a36Sopenharmony_ci		return NULL;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return hpio->pages[index];
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_page);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cibool hyperhold_io_add_page(struct hpio *hpio, u32 index, struct page *page)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	if (!CHECK_INITED)
61762306a36Sopenharmony_ci		return false;
61862306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
61962306a36Sopenharmony_ci		return false;
62062306a36Sopenharmony_ci	if (!CHECK(page, "page is null!\n"))
62162306a36Sopenharmony_ci		return false;
62262306a36Sopenharmony_ci	if (!CHECK_BOUND(index, 0, hpio->nr_page - 1))
62362306a36Sopenharmony_ci		return false;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	get_page(page);
62662306a36Sopenharmony_ci	atomic64_add(PAGE_SIZE, &mem_used);
62762306a36Sopenharmony_ci	BUG_ON(hpio->pages[index]);
62862306a36Sopenharmony_ci	hpio->pages[index] = page;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return true;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_add_page);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ciu32 hyperhold_io_nr_page(struct hpio *hpio)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	if (!CHECK_INITED)
63762306a36Sopenharmony_ci		return 0;
63862306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
63962306a36Sopenharmony_ci		return 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return hpio->nr_page;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_nr_page);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_civoid *hyperhold_io_private(struct hpio *hpio)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	if (!CHECK_INITED)
64862306a36Sopenharmony_ci		return NULL;
64962306a36Sopenharmony_ci	if (!CHECK(hpio, "hpio is null!\n"))
65062306a36Sopenharmony_ci		return NULL;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	return hpio->private;
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_io_private);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic struct page *get_encrypted_page(struct hp_device *dev, struct page *page, unsigned int op)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct page *encrypted_page = NULL;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if (!dev->ctfm) {
66162306a36Sopenharmony_ci		encrypted_page = page;
66262306a36Sopenharmony_ci		get_page(encrypted_page);
66362306a36Sopenharmony_ci		goto out;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	encrypted_page = alloc_page(GFP_NOIO);
66762306a36Sopenharmony_ci	if (!encrypted_page) {
66862306a36Sopenharmony_ci		pr_err("alloc encrypted page failed!\n");
66962306a36Sopenharmony_ci		goto out;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci	encrypted_page->index = page->index;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* just alloc a new page for read */
67462306a36Sopenharmony_ci	if (!op_is_write(op))
67562306a36Sopenharmony_ci		goto out;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* encrypt page for write */
67862306a36Sopenharmony_ci	if (soft_crypt_page(dev->ctfm, encrypted_page, page, HP_DEV_ENCRYPT)) {
67962306a36Sopenharmony_ci		put_page(encrypted_page);
68062306a36Sopenharmony_ci		encrypted_page = NULL;
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ciout:
68362306a36Sopenharmony_ci	return encrypted_page;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic void put_encrypted_pages(struct bio *bio)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct bio_vec *bv = NULL;
68962306a36Sopenharmony_ci	struct bvec_iter_all iter;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	bio_for_each_segment_all(bv, bio, iter)
69262306a36Sopenharmony_ci		put_page(bv->bv_page);
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic void hp_endio_work(struct work_struct *work)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct hpio *hpio = container_of(work, struct hpio, endio_work);
69862306a36Sopenharmony_ci	struct hp_device *dev = NULL;
69962306a36Sopenharmony_ci	struct bio_vec *bv = NULL;
70062306a36Sopenharmony_ci	struct bvec_iter_all iter;
70162306a36Sopenharmony_ci	struct page *page = NULL;
70262306a36Sopenharmony_ci	u32 ext_size;
70362306a36Sopenharmony_ci	sector_t sec;
70462306a36Sopenharmony_ci	int i;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (op_is_write(hpio->op))
70762306a36Sopenharmony_ci		goto endio;
70862306a36Sopenharmony_ci	ext_size = space_of(hpio->eid)->ext_size;
70962306a36Sopenharmony_ci	dev = device_of(hpio->eid);
71062306a36Sopenharmony_ci	sec = hpio->eid * ext_size / dev->sec_size;
71162306a36Sopenharmony_ci	i = 0;
71262306a36Sopenharmony_ci	bio_for_each_segment_all(bv, hpio->bio, iter) {
71362306a36Sopenharmony_ci		page = bv->bv_page;
71462306a36Sopenharmony_ci		BUG_ON(i >= hpio->nr_page);
71562306a36Sopenharmony_ci		BUG_ON(!hpio->pages[i]);
71662306a36Sopenharmony_ci		if (dev->ctfm)
71762306a36Sopenharmony_ci			BUG_ON(soft_crypt_page(dev->ctfm, hpio->pages[i], page, HP_DEV_DECRYPT));
71862306a36Sopenharmony_ci		sec += PAGE_SIZE / dev->sec_size;
71962306a36Sopenharmony_ci		i++;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ciendio:
72262306a36Sopenharmony_ci	put_encrypted_pages(hpio->bio);
72362306a36Sopenharmony_ci	bio_put(hpio->bio);
72462306a36Sopenharmony_ci	if (hpio->endio)
72562306a36Sopenharmony_ci		hpio->endio(hpio);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void hpio_endio(struct bio *bio)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct hpio *hpio = bio->bi_private;
73162306a36Sopenharmony_ci	struct workqueue_struct *wq = NULL;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	pr_info("hpio %p for eid %u returned %d.\n",
73462306a36Sopenharmony_ci			hpio, hpio->eid, bio->bi_status);
73562306a36Sopenharmony_ci	hpio_set_state(hpio, bio->bi_status ? HPIO_FAIL : HPIO_DONE);
73662306a36Sopenharmony_ci	wq = op_is_write(hpio->op) ? hyperhold.write_wq : hyperhold.read_wq;
73762306a36Sopenharmony_ci	queue_work(wq, &hpio->endio_work);
73862306a36Sopenharmony_ci	atomic64_sub(sizeof(struct bio), &mem_used);
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic int hpio_submit(struct hpio *hpio)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct hp_device *dev = NULL;
74462306a36Sopenharmony_ci	struct bio *bio = NULL;
74562306a36Sopenharmony_ci	struct page *page = NULL;
74662306a36Sopenharmony_ci	u32 ext_size;
74762306a36Sopenharmony_ci	sector_t sec;
74862306a36Sopenharmony_ci	int i;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
75162306a36Sopenharmony_ci	dev = device_of(hpio->eid);
75262306a36Sopenharmony_ci	bio = bio_alloc(dev->bdev, BIO_MAX_VECS,
75362306a36Sopenharmony_ci				 hpio->op, GFP_NOIO);
75462306a36Sopenharmony_ci#else
75562306a36Sopenharmony_ci	bio = bio_alloc(GFP_NOIO, BIO_MAX_PAGES);
75662306a36Sopenharmony_ci#endif
75762306a36Sopenharmony_ci	if (!bio) {
75862306a36Sopenharmony_ci		pr_err("bio alloc failed!\n");
75962306a36Sopenharmony_ci		return -ENOMEM;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	atomic64_add(sizeof(struct bio), &mem_used);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
76462306a36Sopenharmony_ci	bio->bi_opf = hpio->op;
76562306a36Sopenharmony_ci#else
76662306a36Sopenharmony_ci	dev = device_of(hpio->eid);
76762306a36Sopenharmony_ci	bio_set_op_attrs(bio, hpio->op, 0);
76862306a36Sopenharmony_ci#endif
76962306a36Sopenharmony_ci	bio_set_dev(bio, dev->bdev);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	ext_size = space_of(hpio->eid)->ext_size;
77262306a36Sopenharmony_ci	sec = div_u64((u64)hpio->eid * ext_size, dev->sec_size);
77362306a36Sopenharmony_ci	bio->bi_iter.bi_sector = sec;
77462306a36Sopenharmony_ci	for (i = 0; i < hpio->nr_page; i++) {
77562306a36Sopenharmony_ci		if (!hpio->pages[i])
77662306a36Sopenharmony_ci			break;
77762306a36Sopenharmony_ci		hpio->pages[i]->index = sec;
77862306a36Sopenharmony_ci		page = get_encrypted_page(dev, hpio->pages[i], hpio->op);
77962306a36Sopenharmony_ci		if (!page)
78062306a36Sopenharmony_ci			goto err;
78162306a36Sopenharmony_ci		if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
78262306a36Sopenharmony_ci			put_page(page);
78362306a36Sopenharmony_ci			goto err;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci		sec += PAGE_SIZE / dev->sec_size;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	if (dev->blk_key)
78962306a36Sopenharmony_ci		inline_crypt_bio(dev->blk_key, bio);
79062306a36Sopenharmony_ci	bio->bi_private = hpio;
79162306a36Sopenharmony_ci	bio->bi_end_io = hpio_endio;
79262306a36Sopenharmony_ci	hpio->bio = bio;
79362306a36Sopenharmony_ci	submit_bio(bio);
79462306a36Sopenharmony_ci	pr_info("submit hpio %p for eid %u.\n", hpio, hpio->eid);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	return 0;
79762306a36Sopenharmony_cierr:
79862306a36Sopenharmony_ci	put_encrypted_pages(bio);
79962306a36Sopenharmony_ci	bio_put(bio);
80062306a36Sopenharmony_ci	atomic64_sub(sizeof(struct bio), &mem_used);
80162306a36Sopenharmony_ci	return -EIO;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int rw_extent_async(struct hpio *hpio, hp_endio endio, void *priv, unsigned int op)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	int ret = 0;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (!hpio_change_state(hpio, HPIO_INIT, HPIO_SUBMIT))
80962306a36Sopenharmony_ci		return -EAGAIN;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	hpio->private = priv;
81262306a36Sopenharmony_ci	hpio->endio = endio;
81362306a36Sopenharmony_ci	INIT_WORK(&hpio->endio_work, hp_endio_work);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ret = hpio_submit(hpio);
81662306a36Sopenharmony_ci	if (ret) {
81762306a36Sopenharmony_ci		hpio_set_state(hpio, HPIO_FAIL);
81862306a36Sopenharmony_ci		hpio_complete(hpio);
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return ret;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ciint hyperhold_write_async(struct hpio *hpio, hp_endio endio, void *priv)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	if (!CHECK_ENABLE) {
82762306a36Sopenharmony_ci		hpio_set_state(hpio, HPIO_FAIL);
82862306a36Sopenharmony_ci		hpio_complete(hpio);
82962306a36Sopenharmony_ci		return -EINVAL;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	BUG_ON(!op_is_write(hpio->op));
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	return rw_extent_async(hpio, endio, priv, REQ_OP_WRITE);
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_write_async);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ciint hyperhold_read_async(struct hpio *hpio, hp_endio endio, void *priv)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	if (!CHECK_INITED) {
84162306a36Sopenharmony_ci		hpio_set_state(hpio, HPIO_FAIL);
84262306a36Sopenharmony_ci		hpio_complete(hpio);
84362306a36Sopenharmony_ci		return -EINVAL;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (op_is_write(hpio->op))
84762306a36Sopenharmony_ci		return -EAGAIN;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return rw_extent_async(hpio, endio, priv, REQ_OP_READ);
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ciEXPORT_SYMBOL(hyperhold_read_async);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cimodule_init(hyperhold_init)
85462306a36Sopenharmony_cimodule_exit(hyperhold_exit)
855