1// SPDX-License-Identifier: GPL-2.0
2/*
3 * drivers/hyperhold/hp_space.c
4 *
5 * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd.
6 */
7
8#define pr_fmt(fmt) "[HYPERHOLD]" fmt
9
10#include <linux/mm.h>
11
12#include "hp_space.h"
13
14atomic64_t spc_mem = ATOMIC64_INIT(0);
15
16u64 space_memory(void)
17{
18	return atomic64_read(&spc_mem);
19}
20
21void deinit_space(struct hp_space *spc)
22{
23	kvfree(spc->bitmap);
24	atomic64_sub(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
25	spc->ext_size = 0;
26	spc->nr_ext = 0;
27	atomic_set(&spc->last_alloc_bit, 0);
28	atomic_set(&spc->nr_alloced, 0);
29
30	pr_info("hyperhold space deinited.\n");
31}
32
33bool init_space(struct hp_space *spc, u64 dev_size, u32 ext_size)
34{
35	if (ext_size & (PAGE_SIZE - 1)) {
36		pr_err("extent size %u do not align to page size %lu!", ext_size, PAGE_SIZE);
37		return false;
38	}
39	if (dev_size & (ext_size - 1)) {
40		pr_err("device size %llu do not align to extent size %u!", dev_size, ext_size);
41		return false;
42	}
43	spc->ext_size = ext_size;
44	spc->nr_ext = div_u64(dev_size, ext_size);
45	atomic_set(&spc->last_alloc_bit, 0);
46	atomic_set(&spc->nr_alloced, 0);
47	init_waitqueue_head(&spc->empty_wq);
48	spc->bitmap = kvzalloc(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), GFP_KERNEL);
49	if (!spc->bitmap) {
50		pr_err("hyperhold bitmap alloc failed.\n");
51		return false;
52	}
53	atomic64_add(BITS_TO_LONGS(spc->nr_ext) * sizeof(long), &spc_mem);
54
55	pr_info("hyperhold space init succ, capacity = %u x %u.\n", ext_size, spc->nr_ext);
56
57	return true;
58}
59
60int alloc_eid(struct hp_space *spc)
61{
62	u32 bit;
63	u32 last_bit;
64
65retry:
66	last_bit = atomic_read(&spc->last_alloc_bit);
67	bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, last_bit);
68	if (bit == spc->nr_ext)
69		bit = find_next_zero_bit(spc->bitmap, spc->nr_ext, 0);
70	if (bit == spc->nr_ext)
71		goto full;
72	if (test_and_set_bit(bit, spc->bitmap))
73		goto retry;
74
75	atomic_set(&spc->last_alloc_bit, bit);
76	atomic_inc(&spc->nr_alloced);
77
78	pr_info("hyperhold alloc extent %u.\n", bit);
79
80	return bit;
81full:
82	pr_err("hyperhold space is full.\n");
83
84	return -ENOSPC;
85}
86
87void free_eid(struct hp_space *spc, u32 eid)
88{
89	if (!test_and_clear_bit(eid, spc->bitmap)) {
90		pr_err("eid is not alloced!\n");
91		BUG();
92		return;
93	}
94	if (atomic_dec_and_test(&spc->nr_alloced)) {
95		pr_info("notify space empty.\n");
96		wake_up(&spc->empty_wq);
97	}
98	pr_info("hyperhold free extent %u.\n", eid);
99}
100
101static void dump_space(struct hp_space *spc)
102{
103	u32 i = 0;
104
105	pr_info("dump alloced extent in space.\n");
106	for (i = 0; i < spc->nr_ext; i++)
107		if (test_bit(i, spc->bitmap))
108			pr_info("alloced eid %u.\n", i);
109}
110
111bool wait_for_space_empty(struct hp_space *spc, bool force)
112{
113	if (!atomic_read(&spc->nr_alloced))
114		return true;
115	if (!force)
116		return false;
117
118	dump_space(spc);
119	wait_event(spc->empty_wq, !atomic_read(&spc->nr_alloced));
120
121	return true;
122}
123