18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/hyperhold/hp_space.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "[HYPERHOLD]" fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "hp_space.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ciatomic64_t spc_mem = ATOMIC64_INIT(0);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciu64 space_memory(void)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	return atomic64_read(&spc_mem);
198c2ecf20Sopenharmony_ci}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_civoid deinit_space(struct hp_space *spc)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	kvfree(spc->bitmap);
248c2ecf20Sopenharmony_ci	atomic64_sub(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
258c2ecf20Sopenharmony_ci	spc->ext_size = 0;
268c2ecf20Sopenharmony_ci	spc->nr_ext = 0;
278c2ecf20Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, 0);
288c2ecf20Sopenharmony_ci	atomic_set(&spc->nr_alloced, 0);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	pr_info("hyperhold space deinited.\n");
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cibool init_space(struct hp_space *spc, u64 dev_size, u32 ext_size)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	if (ext_size & (PAGE_SIZE - 1)) {
368c2ecf20Sopenharmony_ci		pr_err("extent size %u do not align to page size %lu!", ext_size, PAGE_SIZE);
378c2ecf20Sopenharmony_ci		return false;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci	if (dev_size & (ext_size - 1)) {
408c2ecf20Sopenharmony_ci		pr_err("device size %llu do not align to extent size %u!", dev_size, ext_size);
418c2ecf20Sopenharmony_ci		return false;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci	spc->ext_size = ext_size;
448c2ecf20Sopenharmony_ci	spc->nr_ext = div_u64(dev_size, ext_size);
458c2ecf20Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, 0);
468c2ecf20Sopenharmony_ci	atomic_set(&spc->nr_alloced, 0);
478c2ecf20Sopenharmony_ci	init_waitqueue_head(&spc->empty_wq);
488c2ecf20Sopenharmony_ci	spc->bitmap = kvzalloc(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!spc->bitmap) {
508c2ecf20Sopenharmony_ci		pr_err("hyperhold bitmap alloc failed.\n");
518c2ecf20Sopenharmony_ci		return false;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci	atomic64_add(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	pr_info("hyperhold space init succ, capacity = %u x %u.\n", ext_size, spc->nr_ext);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return true;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint alloc_eid(struct hp_space *spc)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u32 bit;
638c2ecf20Sopenharmony_ci	u32 last_bit;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciretry:
668c2ecf20Sopenharmony_ci	last_bit = atomic_read(&spc->last_alloc_bit);
678c2ecf20Sopenharmony_ci	bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, last_bit);
688c2ecf20Sopenharmony_ci	if (bit == spc->nr_ext)
698c2ecf20Sopenharmony_ci		bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, 0);
708c2ecf20Sopenharmony_ci	if (bit == spc->nr_ext)
718c2ecf20Sopenharmony_ci		goto full;
728c2ecf20Sopenharmony_ci	if (test_and_set_bit(bit, spc->bitmap))
738c2ecf20Sopenharmony_ci		goto retry;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, bit);
768c2ecf20Sopenharmony_ci	atomic_inc(&spc->nr_alloced);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	pr_info("hyperhold alloc extent %u.\n", bit);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return bit;
818c2ecf20Sopenharmony_cifull:
828c2ecf20Sopenharmony_ci	pr_err("hyperhold space is full.\n");
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return -ENOSPC;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_civoid free_eid(struct hp_space *spc, u32 eid)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(eid, spc->bitmap)) {
908c2ecf20Sopenharmony_ci		pr_err("eid is not alloced!\n");
918c2ecf20Sopenharmony_ci		BUG();
928c2ecf20Sopenharmony_ci		return;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&spc->nr_alloced)) {
958c2ecf20Sopenharmony_ci		pr_info("notify space empty.\n");
968c2ecf20Sopenharmony_ci		wake_up(&spc->empty_wq);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	pr_info("hyperhold free extent %u.\n", eid);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void dump_space(struct hp_space *spc)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u32 i = 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	pr_info("dump alloced extent in space.\n");
1068c2ecf20Sopenharmony_ci	for (i = 0; i < spc->nr_ext; i++)
1078c2ecf20Sopenharmony_ci		if (test_bit(i, spc->bitmap))
1088c2ecf20Sopenharmony_ci			pr_info("alloced eid %u.\n", i);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cibool wait_for_space_empty(struct hp_space *spc, bool force)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	if (!atomic_read(&spc->nr_alloced))
1148c2ecf20Sopenharmony_ci		return true;
1158c2ecf20Sopenharmony_ci	if (!force)
1168c2ecf20Sopenharmony_ci		return false;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	dump_space(spc);
1198c2ecf20Sopenharmony_ci	wait_event(spc->empty_wq, !atomic_read(&spc->nr_alloced));
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return true;
1228c2ecf20Sopenharmony_ci}
123