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:
Ernesto A. Fernández
2019-10-09 21:37:55 -03:00
parent fa41aac375
commit 06af0392cf
5 changed files with 95 additions and 1 deletions
+12 -1
View File
@@ -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);
}
+58
View File
@@ -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
+1
View File
@@ -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,
+23
View File
@@ -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
+1
View File
@@ -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,