dm array: add dm_array_new()
dm_array_new() creates a new, populated array more efficiently than starting with an empty one and resizing. Signed-off-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
b88efd43f9
commit
dd6a77d998
|
@ -277,6 +277,48 @@ static int insert_ablock(struct dm_array_info *info, uint64_t index,
|
|||
return dm_btree_insert(&info->btree_info, *root, &index, &block_le, root);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
static int __shadow_ablock(struct dm_array_info *info, dm_block_t b,
|
||||
struct dm_block **block, struct array_block **ab)
|
||||
{
|
||||
int inc;
|
||||
int r = dm_tm_shadow_block(info->btree_info.tm, b,
|
||||
&array_validator, block, &inc);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
*ab = dm_block_data(*block);
|
||||
if (inc)
|
||||
inc_ablock_entries(info, *ab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The shadow op will often be a noop. Only insert if it really
|
||||
* copied data.
|
||||
*/
|
||||
static int __reinsert_ablock(struct dm_array_info *info, unsigned index,
|
||||
struct dm_block *block, dm_block_t b,
|
||||
dm_block_t *root)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if (dm_block_location(block) != b) {
|
||||
/*
|
||||
* dm_tm_shadow_block will have already decremented the old
|
||||
* block, but it is still referenced by the btree. We
|
||||
* increment to stop the insert decrementing it below zero
|
||||
* when overwriting the old value.
|
||||
*/
|
||||
dm_tm_inc(info->btree_info.tm, b);
|
||||
r = insert_ablock(info, index, block, root);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up an array block in the btree. Then shadows it, and updates the
|
||||
* btree to point to this new shadow. 'root' is an input/output parameter
|
||||
|
@ -286,49 +328,21 @@ static int shadow_ablock(struct dm_array_info *info, dm_block_t *root,
|
|||
unsigned index, struct dm_block **block,
|
||||
struct array_block **ab)
|
||||
{
|
||||
int r, inc;
|
||||
int r;
|
||||
uint64_t key = index;
|
||||
dm_block_t b;
|
||||
__le64 block_le;
|
||||
|
||||
/*
|
||||
* lookup
|
||||
*/
|
||||
r = dm_btree_lookup(&info->btree_info, *root, &key, &block_le);
|
||||
if (r)
|
||||
return r;
|
||||
b = le64_to_cpu(block_le);
|
||||
|
||||
/*
|
||||
* shadow
|
||||
*/
|
||||
r = dm_tm_shadow_block(info->btree_info.tm, b,
|
||||
&array_validator, block, &inc);
|
||||
r = __shadow_ablock(info, b, block, ab);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
*ab = dm_block_data(*block);
|
||||
if (inc)
|
||||
inc_ablock_entries(info, *ab);
|
||||
|
||||
/*
|
||||
* Reinsert.
|
||||
*
|
||||
* The shadow op will often be a noop. Only insert if it really
|
||||
* copied data.
|
||||
*/
|
||||
if (dm_block_location(*block) != b) {
|
||||
/*
|
||||
* dm_tm_shadow_block will have already decremented the old
|
||||
* block, but it is still referenced by the btree. We
|
||||
* increment to stop the insert decrementing it below zero
|
||||
* when overwriting the old value.
|
||||
*/
|
||||
dm_tm_inc(info->btree_info.tm, b);
|
||||
r = insert_ablock(info, index, *block, root);
|
||||
}
|
||||
|
||||
return r;
|
||||
return __reinsert_ablock(info, index, *block, b, root);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -681,6 +695,72 @@ int dm_array_resize(struct dm_array_info *info, dm_block_t root,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dm_array_resize);
|
||||
|
||||
static int populate_ablock_with_values(struct dm_array_info *info, struct array_block *ab,
|
||||
value_fn fn, void *context, unsigned base, unsigned new_nr)
|
||||
{
|
||||
int r;
|
||||
unsigned i;
|
||||
uint32_t nr_entries;
|
||||
struct dm_btree_value_type *vt = &info->value_type;
|
||||
|
||||
BUG_ON(le32_to_cpu(ab->nr_entries));
|
||||
BUG_ON(new_nr > le32_to_cpu(ab->max_entries));
|
||||
|
||||
nr_entries = le32_to_cpu(ab->nr_entries);
|
||||
for (i = 0; i < new_nr; i++) {
|
||||
r = fn(base + i, element_at(info, ab, i), context);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
if (vt->inc)
|
||||
vt->inc(vt->context, element_at(info, ab, i));
|
||||
}
|
||||
|
||||
ab->nr_entries = cpu_to_le32(new_nr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_array_new(struct dm_array_info *info, dm_block_t *root,
|
||||
uint32_t size, value_fn fn, void *context)
|
||||
{
|
||||
int r;
|
||||
struct dm_block *block;
|
||||
struct array_block *ab;
|
||||
unsigned block_index, end_block, size_of_block, max_entries;
|
||||
|
||||
r = dm_array_empty(info, root);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
size_of_block = dm_bm_block_size(dm_tm_get_bm(info->btree_info.tm));
|
||||
max_entries = calc_max_entries(info->value_type.size, size_of_block);
|
||||
end_block = dm_div_up(size, max_entries);
|
||||
|
||||
for (block_index = 0; block_index != end_block; block_index++) {
|
||||
r = alloc_ablock(info, size_of_block, max_entries, &block, &ab);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
r = populate_ablock_with_values(info, ab, fn, context,
|
||||
block_index * max_entries,
|
||||
min(max_entries, size));
|
||||
if (r) {
|
||||
unlock_ablock(info, block);
|
||||
break;
|
||||
}
|
||||
|
||||
r = insert_ablock(info, block_index, block, root);
|
||||
unlock_ablock(info, block);
|
||||
if (r)
|
||||
break;
|
||||
|
||||
size -= max_entries;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dm_array_new);
|
||||
|
||||
int dm_array_del(struct dm_array_info *info, dm_block_t root)
|
||||
{
|
||||
return dm_btree_del(&info->btree_info, root);
|
||||
|
|
|
@ -111,6 +111,25 @@ int dm_array_resize(struct dm_array_info *info, dm_block_t root,
|
|||
const void *value, dm_block_t *new_root)
|
||||
__dm_written_to_disk(value);
|
||||
|
||||
/*
|
||||
* Creates a new array populated with values provided by a callback
|
||||
* function. This is more efficient than creating an empty array,
|
||||
* resizing, and then setting values since that process incurs a lot of
|
||||
* copying.
|
||||
*
|
||||
* Assumes 32bit values for now since it's only used by the cache hint
|
||||
* array.
|
||||
*
|
||||
* info - describes the array
|
||||
* root - the root block of the array on disk
|
||||
* size - the number of entries in the array
|
||||
* fn - the callback
|
||||
* context - passed to the callback
|
||||
*/
|
||||
typedef int (*value_fn)(uint32_t index, void *value_le, void *context);
|
||||
int dm_array_new(struct dm_array_info *info, dm_block_t *root,
|
||||
uint32_t size, value_fn fn, void *context);
|
||||
|
||||
/*
|
||||
* Frees a whole array. The value_type's decrement operation will be called
|
||||
* for all values in the array
|
||||
|
|
Loading…
Reference in New Issue