diff options
author | Kent Overstreet <kmo@daterainc.com> | 2013-12-18 01:49:08 -0500 |
---|---|---|
committer | Kent Overstreet <kmo@daterainc.com> | 2014-01-08 16:05:10 -0500 |
commit | 78b77bf8b20431f8ad8a4db7e3120103bd922337 (patch) | |
tree | 58ba4fd6e8a069a10a23562275a97554dcde89f9 /drivers/md | |
parent | 88b9f8c426f35e04738220c1bc05dd1ea1b513a3 (diff) |
bcache: Btree verify code improvements
Used this fixed code to find and fix the bug fixed by
a4d885097b0ac0cd1337f171f2d4b83e946094d4.
Signed-off-by: Kent Overstreet <kmo@daterainc.com>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/bcache/bcache.h | 1 | ||||
-rw-r--r-- | drivers/md/bcache/bset.c | 3 | ||||
-rw-r--r-- | drivers/md/bcache/btree.c | 16 | ||||
-rw-r--r-- | drivers/md/bcache/btree.h | 2 | ||||
-rw-r--r-- | drivers/md/bcache/debug.c | 97 | ||||
-rw-r--r-- | drivers/md/bcache/debug.h | 4 |
6 files changed, 83 insertions, 40 deletions
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index d955a4934616..eb6f2e6927ad 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h | |||
@@ -629,6 +629,7 @@ struct cache_set { | |||
629 | 629 | ||
630 | #ifdef CONFIG_BCACHE_DEBUG | 630 | #ifdef CONFIG_BCACHE_DEBUG |
631 | struct btree *verify_data; | 631 | struct btree *verify_data; |
632 | struct bset *verify_ondisk; | ||
632 | struct mutex verify_lock; | 633 | struct mutex verify_lock; |
633 | #endif | 634 | #endif |
634 | 635 | ||
diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index e51a739f7514..98f0ced236b6 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c | |||
@@ -1060,9 +1060,6 @@ static void __btree_sort(struct btree *b, struct btree_iter *iter, | |||
1060 | btree_mergesort(b, out, iter, fixup, remove_stale); | 1060 | btree_mergesort(b, out, iter, fixup, remove_stale); |
1061 | b->nsets = start; | 1061 | b->nsets = start; |
1062 | 1062 | ||
1063 | if (!fixup && !start && b->written) | ||
1064 | bch_btree_verify(b, out); | ||
1065 | |||
1066 | if (!start && order == b->page_order) { | 1063 | if (!start && order == b->page_order) { |
1067 | /* | 1064 | /* |
1068 | * Our temporary buffer is the same size as the btree node's | 1065 | * Our temporary buffer is the same size as the btree node's |
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 8e2573a009f9..f035ae3b1289 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c | |||
@@ -203,7 +203,7 @@ static uint64_t btree_csum_set(struct btree *b, struct bset *i) | |||
203 | return crc ^ 0xffffffffffffffffULL; | 203 | return crc ^ 0xffffffffffffffffULL; |
204 | } | 204 | } |
205 | 205 | ||
206 | static void bch_btree_node_read_done(struct btree *b) | 206 | void bch_btree_node_read_done(struct btree *b) |
207 | { | 207 | { |
208 | const char *err = "bad btree header"; | 208 | const char *err = "bad btree header"; |
209 | struct bset *i = b->sets[0].data; | 209 | struct bset *i = b->sets[0].data; |
@@ -290,7 +290,7 @@ static void btree_node_read_endio(struct bio *bio, int error) | |||
290 | closure_put(cl); | 290 | closure_put(cl); |
291 | } | 291 | } |
292 | 292 | ||
293 | void bch_btree_node_read(struct btree *b) | 293 | static void bch_btree_node_read(struct btree *b) |
294 | { | 294 | { |
295 | uint64_t start_time = local_clock(); | 295 | uint64_t start_time = local_clock(); |
296 | struct closure cl; | 296 | struct closure cl; |
@@ -478,6 +478,13 @@ void bch_btree_node_write(struct btree *b, struct closure *parent) | |||
478 | 478 | ||
479 | bch_btree_sort_lazy(b); | 479 | bch_btree_sort_lazy(b); |
480 | 480 | ||
481 | /* | ||
482 | * do verify if there was more than one set initially (i.e. we did a | ||
483 | * sort) and we sorted down to a single set: | ||
484 | */ | ||
485 | if (i != b->sets->data && !b->nsets) | ||
486 | bch_btree_verify(b); | ||
487 | |||
481 | if (b->written < btree_blocks(b)) | 488 | if (b->written < btree_blocks(b)) |
482 | bch_bset_init_next(b); | 489 | bch_bset_init_next(b); |
483 | } | 490 | } |
@@ -782,6 +789,8 @@ void bch_btree_cache_free(struct cache_set *c) | |||
782 | #ifdef CONFIG_BCACHE_DEBUG | 789 | #ifdef CONFIG_BCACHE_DEBUG |
783 | if (c->verify_data) | 790 | if (c->verify_data) |
784 | list_move(&c->verify_data->list, &c->btree_cache); | 791 | list_move(&c->verify_data->list, &c->btree_cache); |
792 | |||
793 | free_pages((unsigned long) c->verify_ondisk, ilog2(bucket_pages(c))); | ||
785 | #endif | 794 | #endif |
786 | 795 | ||
787 | list_splice(&c->btree_cache_freeable, | 796 | list_splice(&c->btree_cache_freeable, |
@@ -822,6 +831,9 @@ int bch_btree_cache_alloc(struct cache_set *c) | |||
822 | #ifdef CONFIG_BCACHE_DEBUG | 831 | #ifdef CONFIG_BCACHE_DEBUG |
823 | mutex_init(&c->verify_lock); | 832 | mutex_init(&c->verify_lock); |
824 | 833 | ||
834 | c->verify_ondisk = (void *) | ||
835 | __get_free_pages(GFP_KERNEL, ilog2(bucket_pages(c))); | ||
836 | |||
825 | c->verify_data = mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL); | 837 | c->verify_data = mca_bucket_alloc(c, &ZERO_KEY, GFP_KERNEL); |
826 | 838 | ||
827 | if (c->verify_data && | 839 | if (c->verify_data && |
diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h index 12c99b1a764d..580b01137264 100644 --- a/drivers/md/bcache/btree.h +++ b/drivers/md/bcache/btree.h | |||
@@ -292,7 +292,7 @@ static inline void rw_unlock(bool w, struct btree *b) | |||
292 | (w ? up_write : up_read)(&b->lock); | 292 | (w ? up_write : up_read)(&b->lock); |
293 | } | 293 | } |
294 | 294 | ||
295 | void bch_btree_node_read(struct btree *); | 295 | void bch_btree_node_read_done(struct btree *); |
296 | void bch_btree_node_write(struct btree *, struct closure *); | 296 | void bch_btree_node_write(struct btree *, struct closure *); |
297 | 297 | ||
298 | void bch_btree_set_root(struct btree *); | 298 | void bch_btree_set_root(struct btree *); |
diff --git a/drivers/md/bcache/debug.c b/drivers/md/bcache/debug.c index 473e8d5a7fe1..8887c550d56c 100644 --- a/drivers/md/bcache/debug.c +++ b/drivers/md/bcache/debug.c | |||
@@ -53,18 +53,18 @@ int bch_bkey_to_text(char *buf, size_t size, const struct bkey *k) | |||
53 | 53 | ||
54 | #define p(...) (out += scnprintf(out, end - out, __VA_ARGS__)) | 54 | #define p(...) (out += scnprintf(out, end - out, __VA_ARGS__)) |
55 | 55 | ||
56 | p("%llu:%llu len %llu -> [", KEY_INODE(k), KEY_OFFSET(k), KEY_SIZE(k)); | 56 | p("%llu:%llu len %llu -> [", KEY_INODE(k), KEY_START(k), KEY_SIZE(k)); |
57 | |||
58 | if (KEY_PTRS(k)) | ||
59 | while (1) { | ||
60 | p("%llu:%llu gen %llu", | ||
61 | PTR_DEV(k, i), PTR_OFFSET(k, i), PTR_GEN(k, i)); | ||
62 | |||
63 | if (++i == KEY_PTRS(k)) | ||
64 | break; | ||
65 | 57 | ||
58 | for (i = 0; i < KEY_PTRS(k); i++) { | ||
59 | if (i) | ||
66 | p(", "); | 60 | p(", "); |
67 | } | 61 | |
62 | if (PTR_DEV(k, i) == PTR_CHECK_DEV) | ||
63 | p("check dev"); | ||
64 | else | ||
65 | p("%llu:%llu gen %llu", PTR_DEV(k, i), | ||
66 | PTR_OFFSET(k, i), PTR_GEN(k, i)); | ||
67 | } | ||
68 | 68 | ||
69 | p("]"); | 69 | p("]"); |
70 | 70 | ||
@@ -78,7 +78,7 @@ int bch_bkey_to_text(char *buf, size_t size, const struct bkey *k) | |||
78 | 78 | ||
79 | #ifdef CONFIG_BCACHE_DEBUG | 79 | #ifdef CONFIG_BCACHE_DEBUG |
80 | 80 | ||
81 | static void dump_bset(struct btree *b, struct bset *i) | 81 | static void dump_bset(struct btree *b, struct bset *i, unsigned set) |
82 | { | 82 | { |
83 | struct bkey *k, *next; | 83 | struct bkey *k, *next; |
84 | unsigned j; | 84 | unsigned j; |
@@ -88,7 +88,7 @@ static void dump_bset(struct btree *b, struct bset *i) | |||
88 | next = bkey_next(k); | 88 | next = bkey_next(k); |
89 | 89 | ||
90 | bch_bkey_to_text(buf, sizeof(buf), k); | 90 | bch_bkey_to_text(buf, sizeof(buf), k); |
91 | printk(KERN_ERR "block %u key %zi/%u: %s", bset_block_offset(b, i), | 91 | printk(KERN_ERR "b %u k %zi/%u: %s", set, |
92 | (uint64_t *) k - i->d, i->keys, buf); | 92 | (uint64_t *) k - i->d, i->keys, buf); |
93 | 93 | ||
94 | for (j = 0; j < KEY_PTRS(k); j++) { | 94 | for (j = 0; j < KEY_PTRS(k); j++) { |
@@ -114,50 +114,83 @@ static void bch_dump_bucket(struct btree *b) | |||
114 | 114 | ||
115 | console_lock(); | 115 | console_lock(); |
116 | for (i = 0; i <= b->nsets; i++) | 116 | for (i = 0; i <= b->nsets; i++) |
117 | dump_bset(b, b->sets[i].data); | 117 | dump_bset(b, b->sets[i].data, |
118 | bset_block_offset(b, b->sets[i].data)); | ||
118 | console_unlock(); | 119 | console_unlock(); |
119 | } | 120 | } |
120 | 121 | ||
121 | void bch_btree_verify(struct btree *b, struct bset *new) | 122 | #define for_each_written_bset(b, start, i) \ |
123 | for (i = (start); \ | ||
124 | (void *) i < (void *) (start) + (KEY_SIZE(&b->key) << 9) &&\ | ||
125 | i->seq == (start)->seq; \ | ||
126 | i = (void *) i + set_blocks(i, b->c) * block_bytes(b->c)) | ||
127 | |||
128 | void bch_btree_verify(struct btree *b) | ||
122 | { | 129 | { |
123 | struct btree *v = b->c->verify_data; | 130 | struct btree *v = b->c->verify_data; |
124 | struct closure cl; | 131 | struct bset *ondisk, *sorted, *inmemory; |
125 | closure_init_stack(&cl); | 132 | struct bio *bio; |
126 | 133 | ||
127 | if (!b->c->verify) | 134 | if (!b->c->verify || !b->c->verify_ondisk) |
128 | return; | 135 | return; |
129 | 136 | ||
130 | down(&b->io_mutex); | 137 | down(&b->io_mutex); |
131 | mutex_lock(&b->c->verify_lock); | 138 | mutex_lock(&b->c->verify_lock); |
132 | 139 | ||
140 | ondisk = b->c->verify_ondisk; | ||
141 | sorted = b->c->verify_data->sets->data; | ||
142 | inmemory = b->sets->data; | ||
143 | |||
133 | bkey_copy(&v->key, &b->key); | 144 | bkey_copy(&v->key, &b->key); |
134 | v->written = 0; | 145 | v->written = 0; |
135 | v->level = b->level; | 146 | v->level = b->level; |
136 | 147 | ||
137 | bch_btree_node_read(v); | 148 | bio = bch_bbio_alloc(b->c); |
149 | bio->bi_bdev = PTR_CACHE(b->c, &b->key, 0)->bdev; | ||
150 | bio->bi_iter.bi_sector = PTR_OFFSET(&b->key, 0); | ||
151 | bio->bi_iter.bi_size = KEY_SIZE(&v->key) << 9; | ||
152 | bch_bio_map(bio, sorted); | ||
138 | 153 | ||
139 | if (new->keys != v->sets[0].data->keys || | 154 | submit_bio_wait(REQ_META|READ_SYNC, bio); |
140 | memcmp(new->start, | 155 | bch_bbio_free(bio, b->c); |
141 | v->sets[0].data->start, | 156 | |
142 | (void *) end(new) - (void *) new->start)) { | 157 | memcpy(ondisk, sorted, KEY_SIZE(&v->key) << 9); |
143 | unsigned i, j; | 158 | |
159 | bch_btree_node_read_done(v); | ||
160 | sorted = v->sets->data; | ||
161 | |||
162 | if (inmemory->keys != sorted->keys || | ||
163 | memcmp(inmemory->start, | ||
164 | sorted->start, | ||
165 | (void *) end(inmemory) - (void *) inmemory->start)) { | ||
166 | struct bset *i; | ||
167 | unsigned j; | ||
144 | 168 | ||
145 | console_lock(); | 169 | console_lock(); |
146 | 170 | ||
147 | printk(KERN_ERR "*** original memory node:\n"); | 171 | printk(KERN_ERR "*** in memory:\n"); |
148 | for (i = 0; i <= b->nsets; i++) | 172 | dump_bset(b, inmemory, 0); |
149 | dump_bset(b, b->sets[i].data); | ||
150 | 173 | ||
151 | printk(KERN_ERR "*** sorted memory node:\n"); | 174 | printk(KERN_ERR "*** read back in:\n"); |
152 | dump_bset(b, new); | 175 | dump_bset(v, sorted, 0); |
153 | 176 | ||
154 | printk(KERN_ERR "*** on disk node:\n"); | 177 | for_each_written_bset(b, ondisk, i) { |
155 | dump_bset(v, v->sets[0].data); | 178 | unsigned block = ((void *) i - (void *) ondisk) / |
179 | block_bytes(b->c); | ||
156 | 180 | ||
157 | for (j = 0; j < new->keys; j++) | 181 | printk(KERN_ERR "*** on disk block %u:\n", block); |
158 | if (new->d[j] != v->sets[0].data->d[j]) | 182 | dump_bset(b, i, block); |
183 | } | ||
184 | |||
185 | printk(KERN_ERR "*** block %zu not written\n", | ||
186 | ((void *) i - (void *) ondisk) / block_bytes(b->c)); | ||
187 | |||
188 | for (j = 0; j < inmemory->keys; j++) | ||
189 | if (inmemory->d[j] != sorted->d[j]) | ||
159 | break; | 190 | break; |
160 | 191 | ||
192 | printk(KERN_ERR "b->written %u\n", b->written); | ||
193 | |||
161 | console_unlock(); | 194 | console_unlock(); |
162 | panic("verify failed at %u\n", j); | 195 | panic("verify failed at %u\n", j); |
163 | } | 196 | } |
diff --git a/drivers/md/bcache/debug.h b/drivers/md/bcache/debug.h index 2ede60e31874..08e116e74d36 100644 --- a/drivers/md/bcache/debug.h +++ b/drivers/md/bcache/debug.h | |||
@@ -7,7 +7,7 @@ int bch_bkey_to_text(char *buf, size_t size, const struct bkey *k); | |||
7 | 7 | ||
8 | #ifdef CONFIG_BCACHE_DEBUG | 8 | #ifdef CONFIG_BCACHE_DEBUG |
9 | 9 | ||
10 | void bch_btree_verify(struct btree *, struct bset *); | 10 | void bch_btree_verify(struct btree *); |
11 | void bch_data_verify(struct cached_dev *, struct bio *); | 11 | void bch_data_verify(struct cached_dev *, struct bio *); |
12 | int __bch_count_data(struct btree *); | 12 | int __bch_count_data(struct btree *); |
13 | void __bch_check_keys(struct btree *, const char *, ...); | 13 | void __bch_check_keys(struct btree *, const char *, ...); |
@@ -20,7 +20,7 @@ void bch_btree_iter_next_check(struct btree_iter *); | |||
20 | 20 | ||
21 | #else /* DEBUG */ | 21 | #else /* DEBUG */ |
22 | 22 | ||
23 | static inline void bch_btree_verify(struct btree *b, struct bset *i) {} | 23 | static inline void bch_btree_verify(struct btree *b) {} |
24 | static inline void bch_data_verify(struct cached_dev *dc, struct bio *bio) {} | 24 | static inline void bch_data_verify(struct cached_dev *dc, struct bio *bio) {} |
25 | static inline int __bch_count_data(struct btree *b) { return -1; } | 25 | static inline int __bch_count_data(struct btree *b) { return -1; } |
26 | static inline void __bch_check_keys(struct btree *b, const char *fmt, ...) {} | 26 | static inline void __bch_check_keys(struct btree *b, const char *fmt, ...) {} |