Skip to content

Commit

Permalink
dm vdo: read refcount blocks in large chunks at load time
Browse files Browse the repository at this point in the history
At load time we want to bring in all the refcount data before the
device is ready. We do that using a new pool of larger-size metadata
VIOs. The larger-size VIO pool is released after loading finishes.

During normal operation, we only save individual blocks after
they've been modified, so we continue to use the 4kB metadata VIO
pool.

Signed-off-by: Ken Raeburn <[email protected]>
Signed-off-by: Matthew Sakai <[email protected]>
  • Loading branch information
raeburn authored and lorelei-sakai committed Jan 15, 2025
1 parent be27a72 commit 3d21974
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 17 deletions.
63 changes: 47 additions & 16 deletions drivers/md/dm-vdo/slab-depot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ static void handle_io_error(struct vdo_completion *completion)

vio_record_metadata_io_error(vio);
return_vio_to_pool(vio_as_pooled_vio(vio));
slab->active_count--;
slab->active_count -= vio->io_size / VDO_BLOCK_SIZE;
vdo_enter_read_only_mode(slab->allocator->depot->vdo, result);
check_if_slab_drained(slab);
}
Expand Down Expand Up @@ -2239,13 +2239,20 @@ static void finish_reference_block_load(struct vdo_completion *completion)
struct pooled_vio *pooled = vio_as_pooled_vio(vio);
struct reference_block *block = completion->parent;
struct vdo_slab *slab = block->slab;
unsigned int block_count = vio->io_size / VDO_BLOCK_SIZE;
unsigned int i;
char *data = vio->data;

for (i = 0; i < block_count; i++, block++, data += VDO_BLOCK_SIZE) {
struct packed_reference_block *packed = (struct packed_reference_block *) data;

unpack_reference_block((struct packed_reference_block *) vio->data, block);
unpack_reference_block(packed, block);
clear_provisional_references(block);
slab->free_blocks -= block->allocated_count;
}
return_vio_to_pool(pooled);
slab->active_count--;
clear_provisional_references(block);
slab->active_count -= block_count;

slab->free_blocks -= block->allocated_count;
check_if_slab_drained(slab);
}

Expand All @@ -2259,23 +2266,25 @@ static void load_reference_block_endio(struct bio *bio)
}

/**
* load_reference_block() - After a block waiter has gotten a VIO from the VIO pool, load the
* block.
* @waiter: The waiter of the block to load.
* load_reference_block_group() - After a block waiter has gotten a VIO from the VIO pool, load
* a set of blocks.
* @waiter: The waiter of the first block to load.
* @context: The VIO returned by the pool.
*/
static void load_reference_block(struct vdo_waiter *waiter, void *context)
static void load_reference_block_group(struct vdo_waiter *waiter, void *context)
{
struct pooled_vio *pooled = context;
struct vio *vio = &pooled->vio;
struct reference_block *block =
container_of(waiter, struct reference_block, waiter);
size_t block_offset = (block - block->slab->reference_blocks);
u32 block_offset = block - block->slab->reference_blocks;
u32 max_block_count = block->slab->reference_block_count - block_offset;
u32 block_count = min_t(int, vio->block_count, max_block_count);

vio->completion.parent = block;
vdo_submit_metadata_vio(vio, block->slab->ref_counts_origin + block_offset,
load_reference_block_endio, handle_io_error,
REQ_OP_READ);
vdo_submit_metadata_vio_with_size(vio, block->slab->ref_counts_origin + block_offset,
load_reference_block_endio, handle_io_error,
REQ_OP_READ, block_count * VDO_BLOCK_SIZE);
}

/**
Expand All @@ -2285,14 +2294,21 @@ static void load_reference_block(struct vdo_waiter *waiter, void *context)
static void load_reference_blocks(struct vdo_slab *slab)
{
block_count_t i;
u64 blocks_per_vio = slab->allocator->refcount_blocks_per_big_vio;
struct vio_pool *pool = slab->allocator->refcount_big_vio_pool;

if (!pool) {
pool = slab->allocator->vio_pool;
blocks_per_vio = 1;
}

slab->free_blocks = slab->block_count;
slab->active_count = slab->reference_block_count;
for (i = 0; i < slab->reference_block_count; i++) {
for (i = 0; i < slab->reference_block_count; i += blocks_per_vio) {
struct vdo_waiter *waiter = &slab->reference_blocks[i].waiter;

waiter->callback = load_reference_block;
acquire_vio_from_pool(slab->allocator->vio_pool, waiter);
waiter->callback = load_reference_block_group;
acquire_vio_from_pool(pool, waiter);
}
}

Expand Down Expand Up @@ -2699,6 +2715,7 @@ static void finish_scrubbing(struct slab_scrubber *scrubber, int result)
vdo_log_info("VDO commencing normal operation");
else if (prior_state == VDO_RECOVERING)
vdo_log_info("Exiting recovery mode");
free_vio_pool(vdo_forget(allocator->refcount_big_vio_pool));
}

/*
Expand Down Expand Up @@ -3990,6 +4007,7 @@ static int __must_check initialize_block_allocator(struct slab_depot *depot,
struct vdo *vdo = depot->vdo;
block_count_t max_free_blocks = depot->slab_config.data_blocks;
unsigned int max_priority = (2 + ilog2(max_free_blocks));
u32 reference_block_count, refcount_reads_needed, refcount_blocks_per_vio;

*allocator = (struct block_allocator) {
.depot = depot,
Expand All @@ -4013,6 +4031,18 @@ static int __must_check initialize_block_allocator(struct slab_depot *depot,
if (result != VDO_SUCCESS)
return result;

/* Initialize the refcount-reading vio pool. */
reference_block_count = vdo_get_saved_reference_count_size(depot->slab_config.slab_blocks);
refcount_reads_needed = DIV_ROUND_UP(reference_block_count, MAX_BLOCKS_PER_VIO);
refcount_blocks_per_vio = DIV_ROUND_UP(reference_block_count, refcount_reads_needed);
allocator->refcount_blocks_per_big_vio = refcount_blocks_per_vio;
result = make_vio_pool(vdo, BLOCK_ALLOCATOR_REFCOUNT_VIO_POOL_SIZE,
allocator->refcount_blocks_per_big_vio, allocator->thread_id,
VIO_TYPE_SLAB_JOURNAL, VIO_PRIORITY_METADATA,
NULL, &allocator->refcount_big_vio_pool);
if (result != VDO_SUCCESS)
return result;

result = initialize_slab_scrubber(allocator);
if (result != VDO_SUCCESS)
return result;
Expand Down Expand Up @@ -4230,6 +4260,7 @@ void vdo_free_slab_depot(struct slab_depot *depot)
uninitialize_allocator_summary(allocator);
uninitialize_scrubber_vio(&allocator->scrubber);
free_vio_pool(vdo_forget(allocator->vio_pool));
free_vio_pool(vdo_forget(allocator->refcount_big_vio_pool));
vdo_free_priority_table(vdo_forget(allocator->prioritized_slabs));
}

Expand Down
13 changes: 12 additions & 1 deletion drivers/md/dm-vdo/slab-depot.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@
enum {
/* The number of vios in the vio pool is proportional to the throughput of the VDO. */
BLOCK_ALLOCATOR_VIO_POOL_SIZE = 128,

/*
* The number of vios in the vio pool used for loading reference count data. A slab's
* refcounts is capped at ~8MB, and we process one at a time in a zone, so 9 should be
* plenty.
*/
BLOCK_ALLOCATOR_REFCOUNT_VIO_POOL_SIZE = 9,
};

/*
Expand Down Expand Up @@ -248,7 +255,7 @@ struct vdo_slab {

/* A list of the dirty blocks waiting to be written out */
struct vdo_wait_queue dirty_blocks;
/* The number of blocks which are currently writing */
/* The number of blocks which are currently reading or writing */
size_t active_count;

/* A waiter object for updating the slab summary */
Expand Down Expand Up @@ -425,6 +432,10 @@ struct block_allocator {

/* The vio pool for reading and writing block allocator metadata */
struct vio_pool *vio_pool;
/* The vio pool for large initial reads of ref count areas */
struct vio_pool *refcount_big_vio_pool;
/* How many ref count blocks are read per vio at initial load */
u32 refcount_blocks_per_big_vio;
/* The dm_kcopyd client for erasing slab journals */
struct dm_kcopyd_client *eraser;
/* Iterator over the slabs to be erased */
Expand Down

0 comments on commit 3d21974

Please sign in to comment.