Skip to content

Commit

Permalink
dm vdo slab-depot: read refcount blocks in large chunks at load time
Browse files Browse the repository at this point in the history
At startup, vdo loads all the reference count data before the device
reports that it is ready. Using a pool of large metadata vios can
improve the startup speed of vdo. The pool of large vios is released
after the device is ready.

During normal operation, reference counts are updated 4kB at a time,
as before.

Signed-off-by: Ken Raeburn <raeburn@redhat.com>
Signed-off-by: Matthew Sakai <msakai@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
  • Loading branch information
Ken Raeburn authored and Mikulas Patocka committed Feb 3, 2025
1 parent f979da5 commit 0ce46f4
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 @@ -3982,6 +3999,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 @@ -4005,6 +4023,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 @@ -4222,6 +4252,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 0ce46f4

Please sign in to comment.