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