// SPDX-License-Identifier: GPL-2.0 /* * drivers/hyperhold/hp_space.c * * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd. */ #define pr_fmt(fmt) "[HYPERHOLD]" fmt #include #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; }