blob: 0d7acf7343638977d5ca69efc0f2f5a830686a64 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* erofs_cache.c
*
* Copyright (C) 2018 HUAWEI, Inc.
* http://www.huawei.com/
* Created by Miao Xie <miaoxie@huawei.com>
*/
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <erofs/defs.h>
#include "erofs_cache.h"
#include "erofs_error.h"
#include "erofs_debug.h"
#include "erofs_io.h"
#include "mkfs_erofs.h"
#include "mkfs_inode.h"
/* The 1st block is used for SUPER BLOCK, so skip it. */
static u64 erofs_current_block = 1;
static LIST_HEAD(erofs_global_blocks);
static LIST_HEAD(erofs_free_blocks);
static LIST_HEAD(erofs_full_blocks);
/*
* Note: Since we reserved the 1st block for super block, so we can reuse
* this block number as a special number, we use it to tell the caller that
* there is NO ENOUGH FREE SPACE!!!!
*
* So the result value:
* !0 : Allocate the blocks successfully, this is block number of
* the 1st block.
* 0 : ENOSPC, There is no enough space.
*/
u32 erofs_alloc_blocks(u32 nblocks)
{
u32 blkaddr;
u64 devlen;
assert(nblocks);
devlen = dev_length();
if (erofs_current_block > (u64)UINT32_MAX ||
erofs_current_block + nblocks > ((u64)UINT32_MAX) + 1 ||
blknr_to_addr(erofs_current_block + nblocks) > devlen) {
erofs_err("There is no enough free space(curr: %llu, need: %u, device blocks: %llu).",
(unsigned long long)erofs_current_block, nblocks,
(unsigned long long)erofs_blknr(devlen));
return 0;
}
blkaddr = (u32)erofs_current_block;
erofs_current_block += nblocks;
return blkaddr;
}
u64 erofs_get_total_blocks(void)
{
return erofs_current_block;
}
block_buffer_t *erofs_alloc_multi_block_buffer(u32 nblocks)
{
block_buffer_t *blk;
block_buffer_t *next;
block_buffer_t *first;
struct list_head blocks;
u32 blkaddr;
u32 i;
int ret;
init_list_head(&blocks);
for (i = 0; i < nblocks; i++) {
blk = (block_buffer_t *)malloc(sizeof(block_buffer_t));
if (!blk) {
erofs_err("Fail to alloc memory for block buffer");
ret = -ENOMEM;
goto free_block_buffer;
}
memset(blk, 0, sizeof(block_buffer_t));
init_list_head(&blk->bb_metadata_list);
list_add_tail(&blk->bb_global_node, &blocks);
}
blkaddr = erofs_alloc_blocks(nblocks);
if (!blkaddr) {
ret = -ENOSPC;
goto free_block_buffer;
}
first = list_first_entry(&blocks, block_buffer_t, bb_global_node);
list_for_each_entry_safe(blk, next, &blocks, bb_global_node) {
blk->bb_blkaddr = blkaddr;
blkaddr++;
list_del(&blk->bb_global_node);
list_add_tail(&blk->bb_global_node, &erofs_global_blocks);
list_add_tail(&blk->bb_alloc_node, &erofs_free_blocks);
}
return first;
free_block_buffer:
list_for_each_entry_safe(blk, next, &blocks, bb_global_node) {
list_del(&blk->bb_global_node);
free(blk);
}
return ERR_PTR(ret);
}
block_buffer_t *erofs_alloc_single_block_buffer(void)
{
return erofs_alloc_multi_block_buffer(1);
}
block_buffer_t *erofs_look_up_free_pos(int request_size)
{
block_buffer_t *blk = NULL;
list_for_each_entry(blk, &erofs_free_blocks, bb_alloc_node) {
if ((request_size + blk->bb_free_slot * EROFS_SLOTSIZE) <
EROFS_BLKSIZE)
return blk;
}
blk = erofs_alloc_single_block_buffer();
return blk;
}
int erofs_flush_all_blocks(void)
{
struct block_buffer *blk;
struct erofs_meta_node *node;
struct erofs_vnode *inode;
struct erofs_index_info *index;
char *erofs_blk_buf;
char *pbuf;
int count;
int ret = 0;
erofs_blk_buf = malloc(EROFS_BLKSIZE);
if (!erofs_blk_buf)
return -ENOMEM;
list_for_each_entry(blk, &erofs_global_blocks, bb_global_node) {
pbuf = erofs_blk_buf;
memset(pbuf, 0, EROFS_BLKSIZE);
list_for_each_entry(node, &blk->bb_metadata_list, m_node) {
switch (node->m_type) {
case EROFS_META_INODE:
inode = (struct erofs_vnode *)node;
count = erofs_write_inode_buffer(inode, pbuf);
break;
case EROFS_META_INDEX:
index = (struct erofs_index_info *)node;
count = erofs_write_index_buffer(index, pbuf);
break;
default:
erofs_err("Wrong metadata type");
assert(0);
}
count = round_up(count, EROFS_SLOTSIZE);
assert(count == node->m_len);
pbuf += count;
}
ret = blk_write(erofs_blk_buf, blk->bb_blkaddr);
if (ret)
break;
}
free(erofs_blk_buf);
return ret;
}
void erofs_put_block_buffer(struct block_buffer *blk)
{
if (blk->bb_free_slot == MAX_NID_INDEX_PER_BLK) {
list_del(&blk->bb_alloc_node);
list_add_tail(&blk->bb_alloc_node, &erofs_full_blocks);
} else if (blk->bb_free_slot > MAX_NID_INDEX_PER_BLK) {
erofs_err("block buffer overflow: free_slot = %d, MAX_NID_INDEX_PER_BLK = %d",
blk->bb_free_slot, MAX_NID_INDEX_PER_BLK);
assert(0);
}
}
int erofs_cache_init(u64 start_blk)
{
erofs_current_block = start_blk;
return 0;
}