From: Mike Snitzer Date: Sun, 19 Jan 2014 04:38:53 +0000 (-0500) Subject: dm thin: preallocate contiguous blocks identified in sorted bio_list X-Git-Url: https://git.fsl.cs.stonybrook.edu/?a=commitdiff_plain;h=7b3afaf8a1cdc0fb61c8169c0b792456952f9c29;p=linux-dmdedup.git dm thin: preallocate contiguous blocks identified in sorted bio_list FIXME: this patch is completely broken, assumptions in __alloc_data_blocks are very flawed. FIXME: make multiblock allocation not fail if partially fulfilled? --- diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 7dfc72ef722..002b15600fe 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -662,6 +662,23 @@ static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m) mempool_free(m, m->tc->pool->mapping_pool); } +static int commit_prepared_block(struct thin_c *tc, + dm_block_t virt_block, dm_block_t data_block) +{ + int r; + + /* + * Commit the prepared block into the mapping btree. + * Any I/O for this block arriving after this point will get + * remapped to it directly. + */ + r = dm_thin_insert_block(tc->td, virt_block, data_block); + if (r) + metadata_operation_failed(tc->pool, "dm_thin_insert_block", r); + + return r; +} + static void process_prepared_mapping(struct dm_thin_new_mapping *m) { struct thin_c *tc = m->tc; @@ -680,14 +697,8 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) goto out; } - /* - * Commit the prepared block into the mapping btree. - * Any I/O for this block arriving after this point will get - * remapped to it directly. - */ - r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block); + r = commit_prepared_block(tc, m->virt_block, m->data_block); if (r) { - metadata_operation_failed(pool, "dm_thin_insert_block", r); cell_error(pool, m->cell); goto out; } @@ -967,12 +978,57 @@ static void check_low_water_mark(struct pool *pool, dm_block_t free_blocks) static void set_pool_mode(struct pool *pool, enum pool_mode new_mode); -static int alloc_data_block(struct thin_c *tc, dm_block_t *result) +struct thin_bio_hints { + dm_block_t bio_virt_block; + struct dm_thin_endio_hook *pbd; /* per-bio-date */ +}; + +static int __alloc_data_blocks(struct thin_c *tc, struct thin_bio_hints *bio_hints, + dm_block_t *result) { int r; - dm_block_t free_blocks; + dm_block_t last_virt_block, virt_block, data_block; struct pool *pool = tc->pool; + /* + * Allocate first block of extent, which is already locked in a cell. + * process_prepared_mapping() saves data_block to mapping btree. + */ + r = dm_pool_alloc_data_block(pool->pmd, &data_block); + if (r) { + metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); + return r; + } + *result = data_block; + + /* + * Now pre-allocate the rest of the blocks needed for this extent. + * + * There is no need to lock these bio-less virt_blocks in a cell because we + * _know_ they will be because they are next on the sorted deferred_bio_list + */ + last_virt_block = bio_hints->bio_virt_block + bio_hints->pbd->blocks_this_allocation; + for (virt_block = bio_hints->bio_virt_block + 1; virt_block < last_virt_block; virt_block++) { + r = dm_pool_alloc_data_block(pool->pmd, &data_block); + if (r) { + metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); + return r; + } + + /* Must commit preallocated data_block to thin dev's mapping btree */ + r = commit_prepared_block(tc, virt_block, data_block); + if (r) + return r; + } + + return 0; +} + +static int check_free_blocks(struct pool *pool, dm_block_t *result) +{ + int r; + dm_block_t free_blocks; + if (WARN_ON(get_pool_mode(pool) != PM_WRITE)) return -EINVAL; @@ -1005,12 +1061,40 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) } } - r = dm_pool_alloc_data_block(pool->pmd, result); - if (r) { - metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); + *result = free_blocks; + return 0; +} + +/* + * Allocate one or more data blocks. If @bio_hints are not NULL the caller + * allows multiple blocks to be allocated (governed by blocks_this_allocation). + */ +static int alloc_data_block(struct thin_c *tc, struct thin_bio_hints *bio_hints, + dm_block_t *result) +{ + int r; + dm_block_t free_blocks; + struct pool *pool = tc->pool; + + if (get_pool_mode(pool) != PM_WRITE) + return -EINVAL; + + r = check_free_blocks(pool, &free_blocks); + if (r) return r; + + if (!bio_hints || bio_hints->pbd->blocks_this_allocation == 1) { + r = dm_pool_alloc_data_block(pool->pmd, result); + if (r) { + metadata_operation_failed(pool, "dm_pool_alloc_data_block", r); + return r; + } + return 0; } + if (free_blocks >= bio_hints->pbd->blocks_this_allocation) + return __alloc_data_blocks(tc, bio_hints, result); + return 0; } @@ -1172,9 +1256,12 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, { int r; dm_block_t data_block; + struct thin_bio_hints bio_hints; struct pool *pool = tc->pool; - r = alloc_data_block(tc, &data_block); + bio_hints.bio_virt_block = block; + bio_hints.pbd = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); + r = alloc_data_block(tc, &bio_hints, &data_block); switch (r) { case 0: schedule_internal_copy(tc, block, lookup_result->block, @@ -1227,6 +1314,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block { int r; dm_block_t data_block; + struct thin_bio_hints bio_hints; struct pool *pool = tc->pool; /* @@ -1250,7 +1338,9 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block return; } - r = alloc_data_block(tc, &data_block); + bio_hints.bio_virt_block = block; + bio_hints.pbd = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); + r = alloc_data_block(tc, &bio_hints, &data_block); switch (r) { case 0: if (tc->origin_dev)