Skip to content

Commit a384259

Browse files
author
Mikulas Patocka
committed
dm-transaction-manager: use red-black trees instead of linear lists
There was reported performance degradation when the shadow map contained too many entries [1]. The shadow map uses 256-bucket hash with linear lists - when there are too many entries, it has quadratic complexity. Meir Elisha proposed to add a module parameter that could configure the size of the hash array - however, this is not ideal because users don't know that they should increase the parameter when they get bad performance. This commit replaces the linear lists with rb-trees (so that there's a hash of rb-trees), they have logarithmic complexity, so it solves the performance degradation. Link: https://patchwork.kernel.org/project/dm-devel/patch/[email protected]/ [1] Reported-by: Meir Elisha <[email protected]> Signed-off-by: Mikulas Patocka <[email protected]>
1 parent cd6521d commit a384259

File tree

1 file changed

+37
-17
lines changed

1 file changed

+37
-17
lines changed

drivers/md/persistent-data/dm-transaction-manager.c

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/export.h>
1414
#include <linux/mutex.h>
1515
#include <linux/hash.h>
16+
#include <linux/rbtree.h>
1617
#include <linux/slab.h>
1718
#include <linux/device-mapper.h>
1819

@@ -77,7 +78,7 @@ static void prefetch_issue(struct prefetch_set *p, struct dm_block_manager *bm)
7778
/*----------------------------------------------------------------*/
7879

7980
struct shadow_info {
80-
struct hlist_node hlist;
81+
struct rb_node node;
8182
dm_block_t where;
8283
};
8384

@@ -95,7 +96,7 @@ struct dm_transaction_manager {
9596
struct dm_space_map *sm;
9697

9798
spinlock_t lock;
98-
struct hlist_head buckets[DM_HASH_SIZE];
99+
struct rb_root buckets[DM_HASH_SIZE];
99100

100101
struct prefetch_set prefetches;
101102
};
@@ -106,14 +107,22 @@ static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b)
106107
{
107108
int r = 0;
108109
unsigned int bucket = dm_hash_block(b, DM_HASH_MASK);
109-
struct shadow_info *si;
110+
struct rb_node **node;
110111

111112
spin_lock(&tm->lock);
112-
hlist_for_each_entry(si, tm->buckets + bucket, hlist)
113-
if (si->where == b) {
113+
node = &tm->buckets[bucket].rb_node;
114+
while (*node) {
115+
struct shadow_info *si =
116+
rb_entry(*node, struct shadow_info, node);
117+
if (b == si->where) {
114118
r = 1;
115119
break;
116120
}
121+
if (b < si->where)
122+
node = &si->node.rb_left;
123+
else
124+
node = &si->node.rb_right;
125+
}
117126
spin_unlock(&tm->lock);
118127

119128
return r;
@@ -130,30 +139,41 @@ static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b)
130139

131140
si = kmalloc(sizeof(*si), GFP_NOIO);
132141
if (si) {
142+
struct rb_node **node, *parent;
133143
si->where = b;
134144
bucket = dm_hash_block(b, DM_HASH_MASK);
145+
135146
spin_lock(&tm->lock);
136-
hlist_add_head(&si->hlist, tm->buckets + bucket);
147+
node = &tm->buckets[bucket].rb_node;
148+
parent = NULL;
149+
while (*node) {
150+
struct shadow_info *si =
151+
rb_entry(*node, struct shadow_info, node);
152+
parent = *node;
153+
if (b < si->where)
154+
node = &si->node.rb_left;
155+
else
156+
node = &si->node.rb_right;
157+
}
158+
rb_link_node(&si->node, parent, node);
159+
rb_insert_color(&si->node, &tm->buckets[bucket]);
137160
spin_unlock(&tm->lock);
138161
}
139162
}
140163

141164
static void wipe_shadow_table(struct dm_transaction_manager *tm)
142165
{
143-
struct shadow_info *si;
144-
struct hlist_node *tmp;
145-
struct hlist_head *bucket;
146-
int i;
166+
unsigned int i;
147167

148168
spin_lock(&tm->lock);
149169
for (i = 0; i < DM_HASH_SIZE; i++) {
150-
bucket = tm->buckets + i;
151-
hlist_for_each_entry_safe(si, tmp, bucket, hlist)
170+
while (!RB_EMPTY_ROOT(&tm->buckets[i])) {
171+
struct shadow_info *si =
172+
rb_entry(tm->buckets[i].rb_node, struct shadow_info, node);
173+
rb_erase(&si->node, &tm->buckets[i]);
152174
kfree(si);
153-
154-
INIT_HLIST_HEAD(bucket);
175+
}
155176
}
156-
157177
spin_unlock(&tm->lock);
158178
}
159179

@@ -162,7 +182,7 @@ static void wipe_shadow_table(struct dm_transaction_manager *tm)
162182
static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
163183
struct dm_space_map *sm)
164184
{
165-
int i;
185+
unsigned int i;
166186
struct dm_transaction_manager *tm;
167187

168188
tm = kmalloc(sizeof(*tm), GFP_KERNEL);
@@ -176,7 +196,7 @@ static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
176196

177197
spin_lock_init(&tm->lock);
178198
for (i = 0; i < DM_HASH_SIZE; i++)
179-
INIT_HLIST_HEAD(tm->buckets + i);
199+
tm->buckets[i] = RB_ROOT;
180200

181201
prefetch_init(&tm->prefetches);
182202

0 commit comments

Comments
 (0)