* drivers/hyperhold/hp_space.c
*
* Copyright (c) 2020-2022 Huawei Technologies Co., Ltd.
*/
#define pr_fmt(fmt) "[HYPERHOLD]" fmt
#include <linux/mm.h>
#include "hp_space.h"
atomic64_t spc_mem = ATOMIC64_INIT(0);
u64 space_memory(void)
{
return atomic64_read(&spc_mem);
}
void deinit_space(struct hp_space *spc)
{
kvfree(spc->bitmap);
atomic64_sub(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
spc->ext_size = 0;
spc->nr_ext = 0;
atomic_set(&spc->last_alloc_bit, 0);
atomic_set(&spc->nr_alloced, 0);
pr_info("hyperhold space deinited.\n");
}
bool init_space(struct hp_space *spc, u64 dev_size, u32 ext_size)
{
if (ext_size & (PAGE_SIZE - 1)) {
pr_err("extent size %u do not align to page size %lu!", ext_size, PAGE_SIZE);
return false;
}
if (dev_size & (ext_size - 1)) {
pr_err("device size %llu do not align to extent size %u!", dev_size, ext_size);
return false;
}
spc->ext_size = ext_size;
spc->nr_ext = div_u64(dev_size, ext_size);
atomic_set(&spc->last_alloc_bit, 0);
atomic_set(&spc->nr_alloced, 0);
init_waitqueue_head(&spc->empty_wq);
spc->bitmap = kvzalloc(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), GFP_KERNEL);
if (!spc->bitmap) {
pr_err("hyperhold bitmap alloc failed.\n");
return false;
}
atomic64_add(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
pr_info("hyperhold space init succ, capacity = %u x %u.\n", ext_size, spc->nr_ext);
return true;
}
int alloc_eid(struct hp_space *spc)
{
u32 bit;
u32 last_bit;
retry:
last_bit = atomic_read(&spc->last_alloc_bit);
bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, last_bit);
if (bit == spc->nr_ext)
bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, 0);
if (bit == spc->nr_ext)
goto full;
if (test_and_set_bit(bit, spc->bitmap))
goto retry;
atomic_set(&spc->last_alloc_bit, bit);
atomic_inc(&spc->nr_alloced);
pr_info("hyperhold alloc extent %u.\n", bit);
return bit;
full:
pr_err("hyperhold space is full.\n");
return -ENOSPC;
}
void free_eid(struct hp_space *spc, u32 eid)
{
if (!test_and_clear_bit(eid, spc->bitmap)) {
pr_err("eid is not alloced!\n");
BUG();
return;
}
if (atomic_dec_and_test(&spc->nr_alloced)) {
pr_info("notify space empty.\n");
wake_up(&spc->empty_wq);
}
pr_info("hyperhold free extent %u.\n", eid);
}
static void dump_space(struct hp_space *spc)
{
u32 i = 0;
pr_info("dump alloced extent in space.\n");
for (i = 0; i < spc->nr_ext; i++)
if (test_bit(i, spc->bitmap))
pr_info("alloced eid %u.\n", i);
}
bool wait_for_space_empty(struct hp_space *spc, bool force)
{
if (!atomic_read(&spc->nr_alloced))
return true;
if (!force)
return false;
dump_space(spc);
wait_event(spc->empty_wq, !atomic_read(&spc->nr_alloced));
return true;
}