aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Jeffery <djeffery@redhat.com>2018-08-07 16:56:00 -0400
committerMike Snitzer <snitzer@redhat.com>2018-08-08 10:41:49 -0400
commit3db2776d9fca45305e6c2065905d9a0e7b2c8212 (patch)
treef88276f29b02ad50a15e72ffcf9c67e395262db0
parent784c9a29e99eb40b842c29ecf1cc3a79e00fb629 (diff)
dm snapshot: improve performance by switching out_of_order_list to rbtree
copy_complete()'s processing of out_of_order_list can result in quadratic complexity in the worst case. As such it was the source of consuming too much cpu and the source of significant loss in performance. Fix this by converting out_of_order_list to an rbtree. This improved a dm-snapshot test copy workload from 32 seconds to 4 seconds. Signed-off-by: David Jeffery <djeffery@redhat.com> Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Tested-by: Brett Hull <bhull@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--drivers/md/dm-snap.c39
1 files changed, 26 insertions, 13 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 97de7a7334d4..6f72ac7bbf9a 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -85,7 +85,7 @@ struct dm_snapshot {
85 * A list of pending exceptions that completed out of order. 85 * A list of pending exceptions that completed out of order.
86 * Protected by kcopyd single-threaded callback. 86 * Protected by kcopyd single-threaded callback.
87 */ 87 */
88 struct list_head out_of_order_list; 88 struct rb_root out_of_order_tree;
89 89
90 mempool_t pending_pool; 90 mempool_t pending_pool;
91 91
@@ -200,7 +200,7 @@ struct dm_snap_pending_exception {
200 /* A sequence number, it is used for in-order completion. */ 200 /* A sequence number, it is used for in-order completion. */
201 sector_t exception_sequence; 201 sector_t exception_sequence;
202 202
203 struct list_head out_of_order_entry; 203 struct rb_node out_of_order_node;
204 204
205 /* 205 /*
206 * For writing a complete chunk, bypassing the copy. 206 * For writing a complete chunk, bypassing the copy.
@@ -1173,7 +1173,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
1173 atomic_set(&s->pending_exceptions_count, 0); 1173 atomic_set(&s->pending_exceptions_count, 0);
1174 s->exception_start_sequence = 0; 1174 s->exception_start_sequence = 0;
1175 s->exception_complete_sequence = 0; 1175 s->exception_complete_sequence = 0;
1176 INIT_LIST_HEAD(&s->out_of_order_list); 1176 s->out_of_order_tree = RB_ROOT;
1177 mutex_init(&s->lock); 1177 mutex_init(&s->lock);
1178 INIT_LIST_HEAD(&s->list); 1178 INIT_LIST_HEAD(&s->list);
1179 spin_lock_init(&s->pe_lock); 1179 spin_lock_init(&s->pe_lock);
@@ -1539,28 +1539,41 @@ static void copy_callback(int read_err, unsigned long write_err, void *context)
1539 pe->copy_error = read_err || write_err; 1539 pe->copy_error = read_err || write_err;
1540 1540
1541 if (pe->exception_sequence == s->exception_complete_sequence) { 1541 if (pe->exception_sequence == s->exception_complete_sequence) {
1542 struct rb_node *next;
1543
1542 s->exception_complete_sequence++; 1544 s->exception_complete_sequence++;
1543 complete_exception(pe); 1545 complete_exception(pe);
1544 1546
1545 while (!list_empty(&s->out_of_order_list)) { 1547 next = rb_first(&s->out_of_order_tree);
1546 pe = list_entry(s->out_of_order_list.next, 1548 while (next) {
1547 struct dm_snap_pending_exception, out_of_order_entry); 1549 pe = rb_entry(next, struct dm_snap_pending_exception,
1550 out_of_order_node);
1548 if (pe->exception_sequence != s->exception_complete_sequence) 1551 if (pe->exception_sequence != s->exception_complete_sequence)
1549 break; 1552 break;
1553 next = rb_next(next);
1550 s->exception_complete_sequence++; 1554 s->exception_complete_sequence++;
1551 list_del(&pe->out_of_order_entry); 1555 rb_erase(&pe->out_of_order_node, &s->out_of_order_tree);
1552 complete_exception(pe); 1556 complete_exception(pe);
1557 cond_resched();
1553 } 1558 }
1554 } else { 1559 } else {
1555 struct list_head *lh; 1560 struct rb_node *parent = NULL;
1561 struct rb_node **p = &s->out_of_order_tree.rb_node;
1556 struct dm_snap_pending_exception *pe2; 1562 struct dm_snap_pending_exception *pe2;
1557 1563
1558 list_for_each_prev(lh, &s->out_of_order_list) { 1564 while (*p) {
1559 pe2 = list_entry(lh, struct dm_snap_pending_exception, out_of_order_entry); 1565 pe2 = rb_entry(*p, struct dm_snap_pending_exception, out_of_order_node);
1560 if (pe2->exception_sequence < pe->exception_sequence) 1566 parent = *p;
1561 break; 1567
1568 BUG_ON(pe->exception_sequence == pe2->exception_sequence);
1569 if (pe->exception_sequence < pe2->exception_sequence)
1570 p = &((*p)->rb_left);
1571 else
1572 p = &((*p)->rb_right);
1562 } 1573 }
1563 list_add(&pe->out_of_order_entry, lh); 1574
1575 rb_link_node(&pe->out_of_order_node, parent, p);
1576 rb_insert_color(&pe->out_of_order_node, &s->out_of_order_tree);
1564 } 1577 }
1565} 1578}
1566 1579