aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorJoe Thornber <ejt@redhat.com>2014-10-06 08:48:51 -0400
committerMike Snitzer <snitzer@redhat.com>2014-11-10 15:25:26 -0500
commit4e420c452b11edf9d510c8180ac66f529e5b6206 (patch)
treed5154f9664cbbc13fa0c521086c04273667f45b4 /drivers/md
parent9b460d3699324d570a4d4161c3741431887f102f (diff)
dm bufio: switch from a huge hash table to an rbtree
Converting over to using an rbtree eliminates a fixed 8MB allocation from vmalloc space for the hash table. Signed-off-by: Joe Thornber <ejt@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-bufio.c97
1 files changed, 54 insertions, 43 deletions
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 0be200b6dbf2..dcaa1d9dfbe4 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -14,6 +14,7 @@
14#include <linux/vmalloc.h> 14#include <linux/vmalloc.h>
15#include <linux/shrinker.h> 15#include <linux/shrinker.h>
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/rbtree.h>
17 18
18#define DM_MSG_PREFIX "bufio" 19#define DM_MSG_PREFIX "bufio"
19 20
@@ -48,14 +49,6 @@
48#define DM_BUFIO_INLINE_VECS 16 49#define DM_BUFIO_INLINE_VECS 16
49 50
50/* 51/*
51 * Buffer hash
52 */
53#define DM_BUFIO_HASH_BITS 20
54#define DM_BUFIO_HASH(block) \
55 ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \
56 ((1 << DM_BUFIO_HASH_BITS) - 1))
57
58/*
59 * Don't try to use kmem_cache_alloc for blocks larger than this. 52 * Don't try to use kmem_cache_alloc for blocks larger than this.
60 * For explanation, see alloc_buffer_data below. 53 * For explanation, see alloc_buffer_data below.
61 */ 54 */
@@ -106,7 +99,7 @@ struct dm_bufio_client {
106 99
107 unsigned minimum_buffers; 100 unsigned minimum_buffers;
108 101
109 struct hlist_head *cache_hash; 102 struct rb_root buffer_tree;
110 wait_queue_head_t free_buffer_wait; 103 wait_queue_head_t free_buffer_wait;
111 104
112 int async_write_error; 105 int async_write_error;
@@ -135,7 +128,7 @@ enum data_mode {
135}; 128};
136 129
137struct dm_buffer { 130struct dm_buffer {
138 struct hlist_node hash_list; 131 struct rb_node node;
139 struct list_head lru_list; 132 struct list_head lru_list;
140 sector_t block; 133 sector_t block;
141 void *data; 134 void *data;
@@ -253,6 +246,53 @@ static LIST_HEAD(dm_bufio_all_clients);
253 */ 246 */
254static DEFINE_MUTEX(dm_bufio_clients_lock); 247static DEFINE_MUTEX(dm_bufio_clients_lock);
255 248
249/*----------------------------------------------------------------
250 * A red/black tree acts as an index for all the buffers.
251 *--------------------------------------------------------------*/
252static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
253{
254 struct rb_node *n = c->buffer_tree.rb_node;
255 struct dm_buffer *b;
256
257 while (n) {
258 b = container_of(n, struct dm_buffer, node);
259
260 if (b->block == block)
261 return b;
262
263 n = (b->block < block) ? n->rb_left : n->rb_right;
264 }
265
266 return NULL;
267}
268
269static void __insert(struct dm_bufio_client *c, struct dm_buffer *b)
270{
271 struct rb_node **new = &c->buffer_tree.rb_node, *parent = NULL;
272 struct dm_buffer *found;
273
274 while (*new) {
275 found = container_of(*new, struct dm_buffer, node);
276
277 if (found->block == b->block) {
278 BUG_ON(found != b);
279 return;
280 }
281
282 parent = *new;
283 new = (found->block < b->block) ?
284 &((*new)->rb_left) : &((*new)->rb_right);
285 }
286
287 rb_link_node(&b->node, parent, new);
288 rb_insert_color(&b->node, &c->buffer_tree);
289}
290
291static void __remove(struct dm_bufio_client *c, struct dm_buffer *b)
292{
293 rb_erase(&b->node, &c->buffer_tree);
294}
295
256/*----------------------------------------------------------------*/ 296/*----------------------------------------------------------------*/
257 297
258static void adjust_total_allocated(enum data_mode data_mode, long diff) 298static void adjust_total_allocated(enum data_mode data_mode, long diff)
@@ -434,7 +474,7 @@ static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty)
434 b->block = block; 474 b->block = block;
435 b->list_mode = dirty; 475 b->list_mode = dirty;
436 list_add(&b->lru_list, &c->lru[dirty]); 476 list_add(&b->lru_list, &c->lru[dirty]);
437 hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]); 477 __insert(b->c, b);
438 b->last_accessed = jiffies; 478 b->last_accessed = jiffies;
439} 479}
440 480
@@ -448,7 +488,7 @@ static void __unlink_buffer(struct dm_buffer *b)
448 BUG_ON(!c->n_buffers[b->list_mode]); 488 BUG_ON(!c->n_buffers[b->list_mode]);
449 489
450 c->n_buffers[b->list_mode]--; 490 c->n_buffers[b->list_mode]--;
451 hlist_del(&b->hash_list); 491 __remove(b->c, b);
452 list_del(&b->lru_list); 492 list_del(&b->lru_list);
453} 493}
454 494
@@ -888,23 +928,6 @@ static void __check_watermark(struct dm_bufio_client *c,
888 __write_dirty_buffers_async(c, 1, write_list); 928 __write_dirty_buffers_async(c, 1, write_list);
889} 929}
890 930
891/*
892 * Find a buffer in the hash.
893 */
894static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
895{
896 struct dm_buffer *b;
897
898 hlist_for_each_entry(b, &c->cache_hash[DM_BUFIO_HASH(block)],
899 hash_list) {
900 dm_bufio_cond_resched();
901 if (b->block == block)
902 return b;
903 }
904
905 return NULL;
906}
907
908/*---------------------------------------------------------------- 931/*----------------------------------------------------------------
909 * Getting a buffer 932 * Getting a buffer
910 *--------------------------------------------------------------*/ 933 *--------------------------------------------------------------*/
@@ -1534,11 +1557,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
1534 r = -ENOMEM; 1557 r = -ENOMEM;
1535 goto bad_client; 1558 goto bad_client;
1536 } 1559 }
1537 c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS); 1560 c->buffer_tree = RB_ROOT;
1538 if (!c->cache_hash) {
1539 r = -ENOMEM;
1540 goto bad_hash;
1541 }
1542 1561
1543 c->bdev = bdev; 1562 c->bdev = bdev;
1544 c->block_size = block_size; 1563 c->block_size = block_size;
@@ -1557,9 +1576,6 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
1557 c->n_buffers[i] = 0; 1576 c->n_buffers[i] = 0;
1558 } 1577 }
1559 1578
1560 for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
1561 INIT_HLIST_HEAD(&c->cache_hash[i]);
1562
1563 mutex_init(&c->lock); 1579 mutex_init(&c->lock);
1564 INIT_LIST_HEAD(&c->reserved_buffers); 1580 INIT_LIST_HEAD(&c->reserved_buffers);
1565 c->need_reserved_buffers = reserved_buffers; 1581 c->need_reserved_buffers = reserved_buffers;
@@ -1633,8 +1649,6 @@ bad_cache:
1633 } 1649 }
1634 dm_io_client_destroy(c->dm_io); 1650 dm_io_client_destroy(c->dm_io);
1635bad_dm_io: 1651bad_dm_io:
1636 vfree(c->cache_hash);
1637bad_hash:
1638 kfree(c); 1652 kfree(c);
1639bad_client: 1653bad_client:
1640 return ERR_PTR(r); 1654 return ERR_PTR(r);
@@ -1661,9 +1675,7 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c)
1661 1675
1662 mutex_unlock(&dm_bufio_clients_lock); 1676 mutex_unlock(&dm_bufio_clients_lock);
1663 1677
1664 for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++) 1678 BUG_ON(!RB_EMPTY_ROOT(&c->buffer_tree));
1665 BUG_ON(!hlist_empty(&c->cache_hash[i]));
1666
1667 BUG_ON(c->need_reserved_buffers); 1679 BUG_ON(c->need_reserved_buffers);
1668 1680
1669 while (!list_empty(&c->reserved_buffers)) { 1681 while (!list_empty(&c->reserved_buffers)) {
@@ -1681,7 +1693,6 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c)
1681 BUG_ON(c->n_buffers[i]); 1693 BUG_ON(c->n_buffers[i]);
1682 1694
1683 dm_io_client_destroy(c->dm_io); 1695 dm_io_client_destroy(c->dm_io);
1684 vfree(c->cache_hash);
1685 kfree(c); 1696 kfree(c);
1686} 1697}
1687EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); 1698EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);