You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
apfs: implement creation of free queue nodes
Extend apfs_create_node() to support the creation of free queue nodes. Define and use apfs_cpoint_data_allocate() to allocate the blocks in the checkpoint data area, and apfs_create_cpoint_map() to register them in the checkpoint-mapping block. In the future I would like to simplify apfs_create_node() with functions such as "apfs_create_ephemeral_object" and "apfs_create_virtual_object" that took care of this stuff. The module will now be able to perform far more writes before getting an error, which will simplify testing. We still need to put a limit to the size of the free queue tree, and actually free the blocks in the bitmap. Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
This commit is contained in:
+12
-1
@@ -186,8 +186,19 @@ static struct apfs_node *apfs_create_node(struct super_block *sb, u32 storage)
|
||||
|
||||
subtype = APFS_OBJECT_TYPE_FSTREE;
|
||||
break;
|
||||
case APFS_OBJ_EPHEMERAL:
|
||||
apfs_cpoint_data_allocate(sb, &bno);
|
||||
oid = le64_to_cpu(msb_raw->nx_next_oid);
|
||||
le64_add_cpu(&msb_raw->nx_next_oid, 1);
|
||||
|
||||
err = apfs_create_cpoint_map(sb, oid, bno);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
subtype = APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE;
|
||||
break;
|
||||
default:
|
||||
/* TODO: physical and ephemeral nodes */
|
||||
/* TODO: physical nodes */
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,64 @@ static int apfs_cpm_lookup_oid(struct super_block *sb,
|
||||
return -EAGAIN; /* The mapping may still be in the next block */
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_create_cpoint_map - Create a checkpoint mapping
|
||||
* @sb: filesystem superblock
|
||||
* @oid: ephemeral object id
|
||||
* @bno: block number
|
||||
*
|
||||
* Only mappings for free queue nodes are supported for now. Returns 0 on
|
||||
* success or a negative error code in case of failure.
|
||||
*/
|
||||
int apfs_create_cpoint_map(struct super_block *sb, u64 oid, u64 bno)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nx_superblock *raw_sb = sbi->s_msb_raw;
|
||||
u64 desc_base = le64_to_cpu(raw_sb->nx_xp_desc_base);
|
||||
u32 desc_index = le32_to_cpu(raw_sb->nx_xp_desc_index);
|
||||
u32 desc_blks = le32_to_cpu(raw_sb->nx_xp_desc_blocks);
|
||||
u32 desc_len = le32_to_cpu(raw_sb->nx_xp_desc_len);
|
||||
struct buffer_head *bh;
|
||||
struct apfs_checkpoint_map_phys *cpm;
|
||||
struct apfs_checkpoint_mapping *map;
|
||||
u64 cpm_bno;
|
||||
u32 cpm_count;
|
||||
int err = 0;
|
||||
|
||||
if (!desc_blks || desc_len < 2)
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
/* Last block in area is superblock; we want the last mapping block */
|
||||
cpm_bno = desc_base + (desc_index + desc_len - 2) % desc_blks;
|
||||
bh = sb_bread(sb, cpm_bno);
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
|
||||
ASSERT(sbi->s_xid == le64_to_cpu(cpm->cpm_o.o_xid));
|
||||
|
||||
cpm_count = le32_to_cpu(cpm->cpm_count);
|
||||
if (cpm_count >= apfs_max_maps_per_block(sb)) { /* TODO */
|
||||
apfs_warn(sb, "creation of cpm blocks not yet supported");
|
||||
err = -ENOSPC;
|
||||
goto fail;
|
||||
}
|
||||
map = &cpm->cpm_map[cpm_count];
|
||||
le32_add_cpu(&cpm->cpm_count, 1);
|
||||
|
||||
map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL |
|
||||
APFS_OBJECT_TYPE_BTREE_NODE);
|
||||
map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE);
|
||||
map->cpm_size = cpu_to_le32(sb->s_blocksize);
|
||||
map->cpm_pad = 0;
|
||||
map->cpm_fs_oid = 0;
|
||||
map->cpm_oid = cpu_to_le64(oid);
|
||||
map->cpm_paddr = cpu_to_le64(bno);
|
||||
|
||||
fail:
|
||||
brelse(bh);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_read_ephemeral_object - Find and map an ephemeral object
|
||||
* @sb: superblock structure
|
||||
|
||||
@@ -91,6 +91,7 @@ extern int apfs_obj_verify_csum(struct super_block *sb,
|
||||
struct apfs_obj_phys *obj);
|
||||
extern void apfs_obj_set_csum(struct super_block *sb,
|
||||
struct apfs_obj_phys *obj);
|
||||
extern int apfs_create_cpoint_map(struct super_block *sb, u64 oid, u64 bno);
|
||||
extern struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb,
|
||||
u64 oid);
|
||||
extern struct buffer_head *apfs_read_object_block(struct super_block *sb,
|
||||
|
||||
@@ -236,6 +236,29 @@ static int apfs_update_mapping_blocks(struct super_block *sb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_cpoint_data_allocate - Allocate a new block in the checkpoint data area
|
||||
* @sb: superblock structure
|
||||
* @bno: on return, the allocated block number
|
||||
*/
|
||||
void apfs_cpoint_data_allocate(struct super_block *sb, u64 *bno)
|
||||
{
|
||||
struct apfs_sb_info *sbi = APFS_SB(sb);
|
||||
struct apfs_nx_superblock *raw_sb = sbi->s_msb_raw;
|
||||
u64 data_base = le64_to_cpu(raw_sb->nx_xp_data_base);
|
||||
u32 data_next = le32_to_cpu(raw_sb->nx_xp_data_next);
|
||||
u32 data_blks = le32_to_cpu(raw_sb->nx_xp_data_blocks);
|
||||
u32 data_len = le32_to_cpu(raw_sb->nx_xp_data_len);
|
||||
|
||||
*bno = data_base + data_next;
|
||||
data_next = (data_next + 1) % data_blks;
|
||||
data_len++;
|
||||
|
||||
ASSERT(sbi->s_xid == le64_to_cpu(raw_sb->nx_o.o_xid));
|
||||
raw_sb->nx_xp_data_next = cpu_to_le32(data_next);
|
||||
raw_sb->nx_xp_data_len = cpu_to_le32(data_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* apfs_checkpoint_start - Start the checkpoint for a new transaction
|
||||
* @sb: superblock structure
|
||||
|
||||
@@ -39,6 +39,7 @@ struct apfs_bh_info {
|
||||
struct list_head list; /* List of buffers in the transaction */
|
||||
};
|
||||
|
||||
extern void apfs_cpoint_data_allocate(struct super_block *sb, u64 *bno);
|
||||
extern int apfs_transaction_start(struct super_block *sb);
|
||||
extern int apfs_transaction_commit(struct super_block *sb);
|
||||
extern int apfs_transaction_join(struct super_block *sb,
|
||||
|
||||
Reference in New Issue
Block a user