162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/hyperhold/hp_space.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/mm.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "hp_space.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciatomic64_t spc_mem = ATOMIC64_INIT(0);
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciu64 space_memory(void)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	return atomic64_read(&spc_mem);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_civoid deinit_space(struct hp_space *spc)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	kvfree(spc->bitmap);
2462306a36Sopenharmony_ci	atomic64_sub(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
2562306a36Sopenharmony_ci	spc->ext_size = 0;
2662306a36Sopenharmony_ci	spc->nr_ext = 0;
2762306a36Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, 0);
2862306a36Sopenharmony_ci	atomic_set(&spc->nr_alloced, 0);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	pr_info("hyperhold space deinited.\n");
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cibool init_space(struct hp_space *spc, u64 dev_size, u32 ext_size)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	if (ext_size & (PAGE_SIZE - 1)) {
3662306a36Sopenharmony_ci		pr_err("extent size %u do not align to page size %lu!", ext_size, PAGE_SIZE);
3762306a36Sopenharmony_ci		return false;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	if (dev_size & (ext_size - 1)) {
4062306a36Sopenharmony_ci		pr_err("device size %llu do not align to extent size %u!", dev_size, ext_size);
4162306a36Sopenharmony_ci		return false;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	spc->ext_size = ext_size;
4462306a36Sopenharmony_ci	spc->nr_ext = div_u64(dev_size, ext_size);
4562306a36Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, 0);
4662306a36Sopenharmony_ci	atomic_set(&spc->nr_alloced, 0);
4762306a36Sopenharmony_ci	init_waitqueue_head(&spc->empty_wq);
4862306a36Sopenharmony_ci	spc->bitmap = kvzalloc(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), GFP_KERNEL);
4962306a36Sopenharmony_ci	if (!spc->bitmap) {
5062306a36Sopenharmony_ci		pr_err("hyperhold bitmap alloc failed.\n");
5162306a36Sopenharmony_ci		return false;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	atomic64_add(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	pr_info("hyperhold space init succ, capacity = %u x %u.\n", ext_size, spc->nr_ext);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return true;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint alloc_eid(struct hp_space *spc)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	u32 bit;
6362306a36Sopenharmony_ci	u32 last_bit;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciretry:
6662306a36Sopenharmony_ci	last_bit = atomic_read(&spc->last_alloc_bit);
6762306a36Sopenharmony_ci	bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, last_bit);
6862306a36Sopenharmony_ci	if (bit == spc->nr_ext)
6962306a36Sopenharmony_ci		bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, 0);
7062306a36Sopenharmony_ci	if (bit == spc->nr_ext)
7162306a36Sopenharmony_ci		goto full;
7262306a36Sopenharmony_ci	if (test_and_set_bit(bit, spc->bitmap))
7362306a36Sopenharmony_ci		goto retry;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	atomic_set(&spc->last_alloc_bit, bit);
7662306a36Sopenharmony_ci	atomic_inc(&spc->nr_alloced);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	pr_info("hyperhold alloc extent %u.\n", bit);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return bit;
8162306a36Sopenharmony_cifull:
8262306a36Sopenharmony_ci	pr_err("hyperhold space is full.\n");
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return -ENOSPC;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid free_eid(struct hp_space *spc, u32 eid)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (!test_and_clear_bit(eid, spc->bitmap)) {
9062306a36Sopenharmony_ci		pr_err("eid is not alloced!\n");
9162306a36Sopenharmony_ci		BUG();
9262306a36Sopenharmony_ci		return;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci	if (atomic_dec_and_test(&spc->nr_alloced)) {
9562306a36Sopenharmony_ci		pr_info("notify space empty.\n");
9662306a36Sopenharmony_ci		wake_up(&spc->empty_wq);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	pr_info("hyperhold free extent %u.\n", eid);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void dump_space(struct hp_space *spc)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	u32 i = 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	pr_info("dump alloced extent in space.\n");
10662306a36Sopenharmony_ci	for (i = 0; i < spc->nr_ext; i++)
10762306a36Sopenharmony_ci		if (test_bit(i, spc->bitmap))
10862306a36Sopenharmony_ci			pr_info("alloced eid %u.\n", i);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cibool wait_for_space_empty(struct hp_space *spc, bool force)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	if (!atomic_read(&spc->nr_alloced))
11462306a36Sopenharmony_ci		return true;
11562306a36Sopenharmony_ci	if (!force)
11662306a36Sopenharmony_ci		return false;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	dump_space(spc);
11962306a36Sopenharmony_ci	wait_event(spc->empty_wq, !atomic_read(&spc->nr_alloced));
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return true;
12262306a36Sopenharmony_ci}
123