diff options
author | Joe Thornber <ejt@redhat.com> | 2014-10-06 08:48:51 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-11-10 15:25:26 -0500 |
commit | 4e420c452b11edf9d510c8180ac66f529e5b6206 (patch) | |
tree | d5154f9664cbbc13fa0c521086c04273667f45b4 /drivers/md | |
parent | 9b460d3699324d570a4d4161c3741431887f102f (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.c | 97 |
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 | ||
137 | struct dm_buffer { | 130 | struct 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 | */ |
254 | static DEFINE_MUTEX(dm_bufio_clients_lock); | 247 | static DEFINE_MUTEX(dm_bufio_clients_lock); |
255 | 248 | ||
249 | /*---------------------------------------------------------------- | ||
250 | * A red/black tree acts as an index for all the buffers. | ||
251 | *--------------------------------------------------------------*/ | ||
252 | static 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 | |||
269 | static 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 | |||
291 | static 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 | ||
258 | static void adjust_total_allocated(enum data_mode data_mode, long diff) | 298 | static 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 | */ | ||
894 | static 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); |
1635 | bad_dm_io: | 1651 | bad_dm_io: |
1636 | vfree(c->cache_hash); | ||
1637 | bad_hash: | ||
1638 | kfree(c); | 1652 | kfree(c); |
1639 | bad_client: | 1653 | bad_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 | } |
1687 | EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); | 1698 | EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); |