Commit dd6f0932 authored by Joe Thornber's avatar Joe Thornber Committed by Luis Henriques

dm btree: fix a recursion depth bug in btree walking code

commit 9b460d36 upstream.

The walk code was using a 'ro_spine' to hold it's locked btree nodes.
But this data structure is designed for the rolling lock scheme, and
as such automatically unlocks blocks that are two steps up the call
chain.  This is not suitable for the simple recursive walk algorithm,
which retraces its steps.

This code is only used by the persistent array code, which in turn is
only used by dm-cache.  In order to trigger it you need to have a
mapping tree that is more than 2 levels deep; which equates to 8-16
million cache blocks.  For instance a 4T ssd with a very small block
size of 32k only just triggers this bug.

The fix just places the locked blocks on the stack, and stops using
the ro_spine altogether.
Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 1438d8cf
...@@ -42,6 +42,12 @@ struct btree_node { ...@@ -42,6 +42,12 @@ struct btree_node {
} __packed; } __packed;
/*
* Locks a block using the btree node validator.
*/
int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result);
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n, void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt); struct dm_btree_value_type *vt);
......
...@@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = { ...@@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = {
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
static int bn_read_lock(struct dm_btree_info *info, dm_block_t b, int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result) struct dm_block **result)
{ {
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result); return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
......
...@@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key); ...@@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key);
* FIXME: We shouldn't use a recursive algorithm when we have limited stack * FIXME: We shouldn't use a recursive algorithm when we have limited stack
* space. Also this only works for single level trees. * space. Also this only works for single level trees.
*/ */
static int walk_node(struct ro_spine *s, dm_block_t block, static int walk_node(struct dm_btree_info *info, dm_block_t block,
int (*fn)(void *context, uint64_t *keys, void *leaf), int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context) void *context)
{ {
int r; int r;
unsigned i, nr; unsigned i, nr;
struct dm_block *node;
struct btree_node *n; struct btree_node *n;
uint64_t keys; uint64_t keys;
r = ro_step(s, block); r = bn_read_lock(info, block, &node);
n = ro_node(s); if (r)
return r;
n = dm_block_data(node);
nr = le32_to_cpu(n->header.nr_entries); nr = le32_to_cpu(n->header.nr_entries);
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) { if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
r = walk_node(s, value64(n, i), fn, context); r = walk_node(info, value64(n, i), fn, context);
if (r) if (r)
goto out; goto out;
} else { } else {
...@@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block, ...@@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block,
} }
out: out:
ro_pop(s); dm_tm_unlock(info->tm, node);
return r; return r;
} }
...@@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root, ...@@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
int (*fn)(void *context, uint64_t *keys, void *leaf), int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context) void *context)
{ {
int r;
struct ro_spine spine;
BUG_ON(info->levels > 1); BUG_ON(info->levels > 1);
return walk_node(info, root, fn, context);
init_ro_spine(&spine, info);
r = walk_node(&spine, root, fn, context);
exit_ro_spine(&spine);
return r;
} }
EXPORT_SYMBOL_GPL(dm_btree_walk); EXPORT_SYMBOL_GPL(dm_btree_walk);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment