| // 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; |
| } |